All fixes
warning
Rule: faviconWeight: 2/100

Fix: Missing favicon

A missing favicon leaves browsers with nothing to render except a generic globe icon. Users navigating 20+ open tabs rely on this 16x16 pixel image to find your application. When users bookmark your page, they get a blank gray square instead of your brand logo. Search engines like Google extract and display favicons directly in mobile and desktop search results—missing one means you forfeit your visual identity in the SERP, directly lowering click-through rates.

The Indxel favicon rule catches this oversight. While it holds a minor severity (weight: 2/100), fixing it requires dropping a single file into your repository. A complete implementation handles standard desktop browsers, Apple Touch Icons for iOS home screens, and high-resolution PNGs for Android Progressive Web Apps (PWAs). Without these explicit files, mobile users saving your web app to their home screen receive a heavily compressed, unreadable 180x180 pixel screenshot of your document body.

How do you detect a missing favicon?

Run npx indxel check to scan your build output for missing favicon references. Indxel parses the <head> of your generated HTML and flags any route lacking a valid shortcut icon link or a root /favicon.ico file.

The CLI outputs warnings in the same format as ESLint — one line per issue, with the file path and rule ID.

$ npx indxel check
 
Scanning 47 routes...
 
app/page.tsx
  1:1  warning  Missing favicon reference in <head> or root directory  favicon
 
app/blog/[slug]/page.tsx
  1:1  warning  Missing favicon reference in <head> or root directory  favicon
 
✖ 2 problems (0 errors, 2 warnings)
  Weight penalty: -2/100
  Current score: 98/100

The favicon rule is a warning, not a critical error. It reduces your overall Indxel score by 2 points. Pages will still index without it, but they will render poorly in search engine results pages and browser UI.

How do you implement the favicon fix?

Place a 32x32 pixel favicon.ico file in your Next.js app/ directory for automatic detection, or define <link> tags manually in raw HTML.

Next.js App Router (File-based Metadata)

The Next.js App Router relies on convention over configuration for metadata files. You do not need to manually write <link> tags in your layout.tsx. Drop the image files into the root of your app/ directory, and Next.js automatically injects the correct HTML during the build step.

Bad: No files in the directory. Next.js outputs a <head> with no icon references.

// Bad directory structure
app/
├── layout.tsx
├── page.tsx
└── globals.css

Good: Place the required icon files directly in the app/ directory.

// Good directory structure
app/
├── favicon.ico        # 32x32px ICO file (Legacy browsers)
├── icon.png           # 192x192px PNG file (Modern browsers/Android)
├── apple-icon.png     # 180x180px PNG file (iOS Home Screen)
├── layout.tsx
└── page.tsx

When you run next build, Next.js detects these files and automatically generates the following HTML in your document <head>:

<link rel="icon" href="/favicon.ico" type="image/x-icon" sizes="32x32">
<link rel="icon" href="/icon.png" type="image/png" sizes="192x192">
<link rel="apple-touch-icon" href="/apple-icon.png" type="image/png" sizes="180x180">

Next.js Pages Router

If you maintain a legacy Next.js Pages Router application, file-based metadata is not supported. You must inject the tags manually into the custom _document.tsx file.

// pages/_document.tsx
import { Html, Head, Main, NextScript } from 'next/document';
 
export default function Document() {
  return (
    <Html lang="en">
      <Head>
        {/* Good: Explicitly define icon paths pointing to the public/ directory */}
        <link rel="icon" href="/favicon.ico" sizes="any" />
        <link rel="icon" href="/icon.png" type="image/png" sizes="192x192" />
        <link rel="apple-touch-icon" href="/apple-icon.png" sizes="180x180" />
      </Head>
      <body>
        <Main />
        <NextScript />
      </body>
    </Html>
  );
}

Place the actual image files in your public/ directory so the Next.js static file server can route them correctly.

Plain HTML

For static sites or frameworks without metadata abstractions, add the <link> elements directly inside the <head> tag of your index.html.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Your App</title>
  
  <!-- Good: Standard HTML implementation -->
  <link rel="icon" href="/favicon.ico" sizes="32x32">
  <link rel="icon" href="/icon.png" type="image/png" sizes="192x192">
  <link rel="apple-touch-icon" href="/apple-icon.png" sizes="180x180">
</head>
<body>
  <!-- App content -->
</body>
</html>

Using the Indxel SDK

If you manage metadata programmatically across hundreds of dynamic routes, use the Indxel SDK's defineSEO function. This method guarantees type safety and ensures your output matches the favicon rule requirements exactly.

// lib/seo.ts
import { defineSEO } from '@indxel/sdk';
 
export const baseSEO = defineSEO({
  title: 'Indxel Documentation',
  description: 'Developer-first SEO infrastructure.',
  // Good: Define icons via the SDK interface
  icons: {
    icon: [
      { url: '/favicon.ico', sizes: '32x32' },
      { url: '/icon.png', type: 'image/png', sizes: '192x192' }
    ],
    apple: [
      { url: '/apple-icon.png', sizes: '180x180', type: 'image/png' }
    ]
  }
});

Pass baseSEO into your Next.js generateMetadata function or your framework's respective head manager.

How do you enforce favicon rules in CI?

Add npx indxel check --ci to your build pipeline to fail the build if metadata rules are violated.

Running checks locally relies on developer discipline. Enforcing them in CI guarantees that no new route ships without proper metadata. The Indxel CLI adds roughly 2 seconds to your build time and exits with code 1 if any errors are found.

Since the favicon rule is a warning by default, you must configure Indxel to treat warnings as failures if you want to strictly enforce it, or update your indxel.config.json to elevate the rule severity.

// indxel.config.json
{
  "rules": {
    "favicon": "error"
  }
}

Add the step to your GitHub Actions workflow file:

# .github/workflows/seo-check.yml
name: Indxel SEO Guard
on: [push, pull_request]
 
jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          
      - name: Install dependencies
        run: npm ci
        
      - name: Build application
        run: npm run build
        
      - name: Check SEO rules
        run: npx indxel check --ci

Use the --diff flag in pull requests (npx indxel check --ci --diff origin/main). This instructs Indxel to only scan routes that changed in the current branch, reducing CI time from seconds to milliseconds on large repositories.

What are the edge cases for favicon implementation?

Serving SVG favicons with incorrect MIME types or omitting the sizes="any" attribute causes rendering failures in older browsers.

SVG Favicons and the sizes="any" fix

Modern browsers support SVG favicons, which look infinitely sharper than .ico files and support CSS media queries for dark mode. However, if you ship an SVG favicon, you must explicitly add sizes="any" to your .ico fallback. Without this, Chrome will download the .ico file instead of the .svg file because it incorrectly assumes the specific 32x32 sizing of the .ico is a better match than the scalable SVG.

<!-- The sizes="any" attribute prevents Chrome from choosing the ICO over the SVG -->
<link rel="icon" href="/favicon.ico" sizes="any">
<link rel="icon" href="/icon.svg" type="image/svg+xml">

Next.js aggressive caching

Browsers cache favicon.ico files aggressively, and Next.js caches static assets in the .next directory. If you replace your favicon.ico file but still see the old one in production, you are hitting a cache layer.

To fix this, append a cache-busting query parameter to your manual <link> tags (/favicon.ico?v=2), or if using Next.js App Router file-based metadata, clear the .next directory locally before your next build:

rm -rf .next
npm run build

Dynamic Route Overrides

If you build a multi-tenant application where users get their own subdomains (e.g., user.yourapp.com), you need dynamic favicons based on the tenant. File-based favicon.ico will not work here. You must use generateMetadata in Next.js to dynamically fetch and inject the tenant's specific logo.

// app/[tenant]/layout.tsx
import { Metadata } from 'next';
 
export async function generateMetadata({ params }): Promise<Metadata> {
  const tenantData = await fetchTenant(params.tenant);
  
  return {
    icons: {
      icon: tenantData.logoUrl, // Injects dynamic URL: https://cdn.yourapp.com/logos/tenant-123.png
      apple: tenantData.appleIconUrl
    }
  };
}

Which related Indxel rules check metadata visuals?

The missing-viewport and missing-og-image rules often fail alongside the favicon rule, as all three dictate how your page renders outside the standard browser viewport.

  • missing-og-image: Validates that your route provides an Open Graph image (og:image). While favicons handle the 16x16 pixel browser tab, the Open Graph image handles the 1200x630 pixel preview card when your link is shared on Twitter, Slack, or Discord.
  • missing-viewport: Ensures the <meta name="viewport"> tag exists. Without this, mobile browsers zoom out to render your site like a desktop page, rendering your mobile-optimized Apple Touch Icon useless.

Frequently asked questions

What size should a favicon be?

favicon.ico should be exactly 32x32 pixels. For modern devices, provide icon.png at 192x192 pixels for Android/PWAs, and apple-icon.png at 180x180 pixels for iOS home screens.

Does Next.js App Router need a <link> tag for favicons?

No. If you place favicon.ico, icon.png, or apple-icon.png directly in the root of the app/ directory, Next.js automatically generates and injects the necessary <link> tags during the build step.

Why is my new favicon not updating in Chrome?

Chrome caches favicons aggressively outside the standard HTTP cache. To force a refresh, navigate directly to yourdomain.com/favicon.ico in a new tab, press Cmd+Shift+R (Mac) or Ctrl+F5 (Windows) for a hard reload, then restart your browser.

Do I need an apple-touch-icon?

Yes. iOS does not use standard favicons when a user pins a website to their home screen. Without a specific 180x180 pixel apple-touch-icon, iOS generates an ugly, compressed screenshot of your webpage to use as the application icon.

Frequently asked questions

What size should a favicon be?

favicon.ico should be 32x32 pixels. For additional sizes: icon.png at 192x192 for Android/PWA, apple-icon.png at 180x180 for iOS.

Catch this before it ships

$npx indxel check --ci
Get startedBrowse all fixes
Indxel

SEO validation that runs in your terminal and blocks bad deploys.

GitHubnpm

Product

  • Documentation
  • Pricing
  • Plus Plan
  • CI/CD Guard
  • Indexation
  • Free Tools
  • Blog

Comparisons

  • vs Semrush
  • vs Ahrefs
  • vs Moz
  • vs Screaming Frog
  • All comparisons

Integrations

  • Vercel
  • GitHub Actions
  • Netlify
  • Docker
  • All integrations

Resources

  • Frameworks & use cases
  • Next.js
  • For freelancers
  • For agencies
  • SEO Glossary

Built with care. MIT Licensed.

PrivacyTermsLegalContact