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.