Favicon ToolsVerifyBuild
Vite · Not showing

Vite Favicon Not Showing or 404? Here's the Fix

Vite copies public/ to the output root and rewrites index.html at build time. A favicon that works in dev and 404s in production nearly always has the public/ segment in its path, or was imported from src and hashed.

Two asset pipelines, one of which you want

Anything imported from src is part of the module graph: Vite inlines it or emits it with a content hash and rewrites the reference. That's right for UI images and wrong for a favicon, which has to answer at a fixed URL.

public/ is the escape hatch. Files there are never resolved, hashed, or inlined. They're copied to the root of the output with their names intact. So public/favicon.ico is served at /favicon.ico, and the href must be exactly that.

The correct link tags

<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" />
<link rel="manifest" href="/site.webmanifest" />

Root-absolute paths, no public/ segment. These live in index.html at the project root, not inside public/.

Vite favicon not showing: FAQ

Why does my Vite favicon only 404 in the production build?

Because the path went through public/ or through an import, both of which change between dev and build. Open the built index.html and the output directory and confirm the href and the file agree.

Do I need to import the favicon anywhere?

No. Files in public/ are deliberately outside the module graph. Import one and Vite emits a second hashed copy, leaving two files where you wanted one and nothing at /favicon.ico.

The favicon 404s when I deploy under a sub-path.

Set base in vite.config.ts. Vite rewrites the asset URLs in index.html at build time, so open the built file afterwards and confirm the icon hrefs carry the prefix before shipping.

Keep going