You just merged a massive pull request. The new site design is live on production. You refresh the page, and the CSS looks perfect—except the browser tab is still stubbornly showing that old, pixelated logo from 2023. If you are pulling your hair out because your favicon not updating after deployment is ruining a big launch, you are not alone.

Static assets like favicons are notoriously sticky. Browsers, proxies, and edge networks all conspire to cache them forever to save bandwidth.

We will skip the basic 'clear your browser cache' advice. If you need help with local browser quirks, check out our guide on forcing a local favicon cache clear. Today, we are focusing on the deployment side: how to force the update for every single user hitting your newly deployed site.

The Quick Fix: Cache Busting via Query String

If you are in a hurry and just need the new icon to show up immediately, use a query string. This is the oldest trick in the book, but it works flawlessly.

Open your main HTML template (like index.html or _document.tsx) and append a version parameter to your favicon URL.

<!-- Change this: -->
<link rel='icon' href='/favicon.svg'>

<!-- To this: -->
<link rel='icon' href='/favicon.svg?v=2'>

By appending ?v=2 (or a timestamp, or a git commit hash), you trick the browser into thinking this is a completely new file. It bypasses the local cache and forces a fresh network request. If you are still having issues after this, you might be facing a deeper path issue, which we cover in our favicon troubleshooting guide.

Why Your Favicon Gets Stuck in Production

If the query string fixed it, great. But why did this happen in the first place? When you deploy a modern web app, your files pass through multiple layers of aggressive caching.

1. CDN Edge Caching

If you host on Vercel, Netlify, Cloudflare, or AWS CloudFront, your static files are distributed to edge nodes globally. These networks look at the file name (favicon.ico) and serve it from memory.

Unless your deployment pipeline explicitly invalidates the CDN cache for static files, the edge node will keep serving the old icon until its Time-To-Live (TTL) expires. This can sometimes take 24 to 48 hours.

2. Build Tool Hashing Misses

Modern bundlers like Vite, Webpack, and Next.js are smart. They append unique hashes to your CSS and JS files (e.g., main.a8b4c.js) every time you build. This guarantees users get the latest code.

However, favicons usually live in the public/ or static/ directory. Build tools often copy these files directly to the output folder without hashing them. The file name remains exactly the same, so the browser sees no reason to download it again.

3. The Service Worker Trap (PWAs)

If your site is a Progressive Web App (PWA), you have a Service Worker intercepting network requests. Service workers are notorious for aggressively caching app shells and icons.

If your sw.js file caches /favicon.png and doesn't have an update mechanism triggered by your new deployment, it will serve the old icon from the Cache Storage API indefinitely—even if you bypass the CDN.

How the Pros Prevent Favicon Caching Issues

Query strings are a great band-aid, but some aggressive corporate proxies actually strip query parameters before caching. The bulletproof method is file name versioning.

Strategy 1: Actual File Versioning

Instead of relying on query strings, physically rename your favicon file when you do a major rebrand.

Look at how GitHub handles their dynamic state icons. When you have unread notifications, GitHub doesn't try to overwrite a cached file. They swap the href attribute to a completely different file path (like a blue dot SVG). Changing the actual path is the only 100% guaranteed way to bypass all caching layers instantly.

Strategy 2: Proper Cache-Control Headers

If you control your server configuration (Nginx, Apache, or a custom Node server), you should set specific Cache-Control headers for your favicons.

# Nginx example for favicons
location ~* \.(ico|png|svg)$ {
    expires 1d;
    add_header Cache-Control 'public, max-age=86400, must-revalidate';
}

Setting a shorter max-age (like 1 day instead of 1 year) ensures that even if an old icon gets cached, it won't haunt your deployments for months.

Final Thoughts

Dealing with a favicon not updating after deployment is a rite of passage for web developers. The cache hierarchy—from the browser to the CDN to the service worker—is designed to make the web fast, but it makes updating static assets incredibly frustrating.

Next time you rebrand, save yourself the headache. Generate a crisp, modern set of icons using Mzu favicondl, physically rename the files to include a version number, and deploy with confidence knowing your users will see the new brand instantly.