实操
How to fix a missing favicon in Next.js
The App Router and the Pages Router expect favicons in different places, and static export adds its own failure modes. Here is the fix.
更新于 2026年3月10日 · 约 3 分钟
Next.js generates the favicon link tags for you — but only if the file is in the right place, and the two routers disagree about where that is. Most “favicon missing in Next.js” reports are one of three things: the file is in public/ when the router expects app/, two strategies are emitting competing tags, or a static export is being served from a subpath.
Where the file goes
| Router | Put the file here | Tags generated? |
|---|---|---|
| App Router | app/favicon.ico | Yes, automatically |
| App Router | app/icon.png (or .svg, .jpg) | Yes, <link rel="icon"> |
| App Router | app/apple-icon.png | Yes, <link rel="apple-touch-icon"> |
| App Router | public/* plus metadata.icons | Only if you declare them |
| Pages Router | public/favicon.ico plus next/head | Only if you write them |
The App Router treats special file names in app/ as conventions. Drop the file in and Next writes the tag for you:
app/
favicon.ico
icon.png
apple-icon.png
layout.tsx
App Router: the metadata export
When you need control — custom paths, several sizes, a manifest — declare it explicitly in layout.tsx:
import type { Metadata } from 'next';
export const metadata: Metadata = {
icons: {
icon: [
{ url: '/favicon-32x32.png', sizes: '32x32', type: 'image/png' },
{ url: '/favicon-16x16.png', sizes: '16x16', type: 'image/png' },
{ url: '/favicon.ico', sizes: 'any' },
],
apple: '/apple-touch-icon.png',
},
manifest: '/site.webmanifest',
};
Do not mix strategies carelessly. If app/icon.png exists and you also list icon in metadata, you emit two competing icon tags, and which one wins is not obvious.
Pages Router
There is no file convention, so you declare the tags yourself. Keep the files in public/ and render the links once in a shared head:
import Head from 'next/head';
export default function MyApp({ Component, pageProps }) {
return (
<>
<Head>
<link rel="icon" href="/favicon.ico" sizes="any" />
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
</Head>
<Component {...pageProps} />
</>
);
}
Putting this in pages/_app.tsx keeps every page consistent, instead of repeating the links per page.
Why static export loses icons
output: 'export' produces plain HTML in out/. Two things commonly go wrong:
- The file is not in the export. Files in
app/are processed by the build. A hard-coded URL in a component points at whatever you typed, but the file is only copied if it lives inpublic/. Inspectout/after the build and confirm the icon is there. - The site is served from a subpath. If the app is deployed under a subroute such as
https://example.com/docs, a link to/favicon.icoresolves to the domain root, not the app. SetbasePathand build the icon URLs from it, or host the icons at the domain root.
// next.config.js
module.exports = {
output: 'export',
basePath: '/docs',
images: { unoptimized: true },
};
Then open out/ and confirm two things: the icon file is present, and the HTML references a path that resolves where you actually deploy.
Common mistakes
Putting favicon.ico in public/ and expecting the App Router convention. The convention only applies to files in app/.
Conflicting definitions. A metadata.icons entry plus an app/icon.png file produces duplicate tags. Remove one.
Hard-coding /favicon.ico under a basePath. Use a base-aware path, or keep the icons at the domain root.
Assuming production matches dev. The dev server serves files the export may not include. Check the built output or the deployed URL.
Blanking on the cache after the fix. A working icon can still show the old one in your browser; that is caching, not a Next.js bug. Version the URL if you need to prove it.
Forgetting the Apple icon. icon.png covers the browser tab; iOS reads apple-icon.png or an apple-touch-icon link. Ship both.
Where this tool fits
Deploy, then run the URL through the favicon checker to see which expected files actually resolve. To generate the full icon set and the tags above in one ZIP first, use the favicon generator.
Frequently asked questions
▸ Where do I put the favicon in the Next.js App Router?
Put favicon.ico, icon.png or apple-icon.png directly in the app directory. Next.js treats those file names as conventions and generates the link tags for you.
▸ Why is my favicon missing after a static export?
Usually the file was not copied into the out directory, or the site is served from a subpath so an absolute /favicon.ico resolves to the domain root instead of the app. Check out after building and set basePath if you deploy under a subroute.
▸ Do I put the favicon in public or in app?
With the App Router, app is enough and gives you automatic tags. Files in public are only used if you declare them yourself through the metadata icons field, or in the Pages Router with next/head.
▸ Why do I get two favicon link tags?
You are mixing strategies: an app/icon.png file convention plus an icons entry in the metadata export both emit a tag. Pick one approach and remove the other.
▸ Does the favicon work in dev but not in production?
The dev server serves files the production export may not include. Always check the built output or the deployed URL, not just the dev server.