You've shipped your first Astro site, deployed it, and noticed the browser tab still shows the default Astro logo. The fix lives in one specific place: your Astro favicon public folder setup.

Unlike Next.js or Nuxt, Astro treats the public folder differently. Files inside it bypass Vite's build pipeline and get copied straight to your final output directory. That's exactly what you want for favicon assets, but it also means you need to understand the mechanics before adding files blindly.

How the Astro Favicon Public Folder Works

Per the Astro project structure docs, the public folder is for files that don't need processing during the build. Favicon assets fall into this category because you don't want Vite to hash, transform, or optimize them — you want them served at predictable URLs so browsers can find them.

Anything you drop into /public becomes available at the root of your deployed site. Put favicon.svg in public/, and it lives at https://yoursite.com/favicon.svg. No imports, no bundler resolution, no fuss.

Prerequisites

Step 1: Generate Your Favicon Files

Before touching the public folder, prepare your assets. For most projects you need four files:

You can generate the full package in one click using Mzu favicondl, which produces all the sizes you need without manual resizing.

Step 2: Drop Files into the Public Folder

Open your project root and locate the public/ directory. Copy your prepared favicon files directly into it. Your folder structure should look like this:

your-astro-project/
├── public/
│   ├── favicon.svg
│   ├── favicon-32x32.png
│   ├── apple-touch-icon.png
│   └── favicon.ico
├── src/
└── astro.config.mjs

Keep filenames lowercase and hyphenated. Browsers don't care about case, but your deployment server might serve different files depending on the OS — stick to one convention.

Step 3: Add Link Tags to Your Layout

Files in public/ won't appear in your HTML automatically. You need to reference them with <link> tags. Open your main layout component (often src/layouts/Layout.astro) and add this block inside the <head>:

<link rel='icon' href='/favicon.svg' type='image/svg+xml'>
<link rel='icon' href='/favicon-32x32.png' sizes='32x32' type='image/png'>
<link rel='apple-touch-icon' href='/apple-touch-icon.png' sizes='180x180'>
<link rel='shortcut icon' href='/favicon.ico'>

This pattern follows the HTML spec for rel='icon' — modern browsers prefer the SVG, older browsers fall back to PNG, and the ICO catches anything stuck in the past.

Step 4: Handle Dark Mode (Optional)

Light favicons can vanish against dark browser chrome. Add a second SVG with inverted colors and a media query to swap them:

<link rel='icon' href='/favicon.svg' type='image/svg+xml'>
<link rel='icon' href='/favicon-dark.svg' type='image/svg+xml' media='(prefers-color-scheme: dark)'>

Place both SVGs in public/. The media attribute tells the browser which one to load based on the user's system theme — no JavaScript required.

Step 5: Verify the Build Output

Before deploying, run your production build and inspect the output. The public folder contents should appear unchanged in dist/:

npm run build
ls dist/

You should see favicon.svg, favicon-32x32.png, and the others sitting at the root of dist/. If they're missing, check that you didn't accidentally nest them inside a subdirectory like public/assets/ — that would put them at /assets/favicon.svg instead of the root.

Common Pitfalls

Putting favicons in src/assets/. Files there get processed by Vite, hashed, and bundled — you lose predictable URLs. Always use public/ for favicon files.

Forgetting to clear the browser cache. After deploying, hard-reload (Ctrl+Shift+R) or test in an incognito window. Browsers aggressively cache favicons, often for days. If you need to force an update across user browsers, see our guide on favicon cache busting best practices.

Missing the sizes attribute. Without it, browsers assume 16x16 and may upscale your PNG, producing a blurry icon. Always declare the actual dimensions.

Using uppercase extensions. Favicon.PNG works on macOS but breaks on case-sensitive Linux servers. Stick to lowercase: favicon.png.

Wrapping Up

Your Astro favicon public folder setup is now complete. The public directory gives you stable, predictable URLs without fighting the build tool — which is exactly what favicons need. Drop in your files, wire up the link tags, verify the output, and your tabs will look professional across every browser.

For a broader view of favicon strategies across static site generators (Next.js, Gatsby, Hugo), check our favicon for static site generators guide. And if your icon isn't showing up after deployment, the fix is usually simpler than you'd expect.