Favicon Caching — Force Browser Refresh After Favicon Change
Browsers aggressively cache favicons, sometimes for days or weeks. Learn how to bust the favicon cache with versioned URLs, HTTP cache headers, and the fastest way to force a...
Browsers cache favicons separately from regular resources — even after a hard refresh. A favicon you changed may not update for days. Cache-bust with a versioned URL in your <link> tag.
Generate your favicon with the Favicon Generator.
Why favicons cache aggressively
Most browsers request /favicon.ico once and cache it based on:
Cache-ControlandExpiresHTTP headers from your server- If no cache headers: browser defaults (often 1 week to 1 year)
- The cache persists across sessions and even incognito in some browsers
The browser doesn’t re-request until the cache expires or the user manually clears it.
Fix: versioned URL in link tag
<!-- Hard-coded version — update v=X whenever you change the favicon -->
<link rel="icon" href="/favicon.ico?v=2" type="image/x-icon">
<link rel="icon" href="/favicon.svg?v=2" type="image/svg+xml">
<link rel="apple-touch-icon" href="/apple-touch-icon.png?v=2">
<!-- Build-time hash (recommended for automated deployments) -->
<link rel="icon" href="/favicon.ico?v=a3f8c2d" type="image/x-icon">
The query string makes the browser treat it as a new URL, bypassing the cached version. The server ignores the query string and serves the same file.
Inject version in build tools
Vite:
// vite.config.js
import { createHash } from 'crypto';
import { readFileSync } from 'fs';
function faviconHash() {
const content = readFileSync('public/favicon.ico');
return createHash('md5').update(content).digest('hex').slice(0, 8);
}
export default {
plugins: [{
name: 'favicon-version',
transformIndexHtml(html) {
const version = faviconHash();
return html.replace(
/href="\/favicon\.ico"/g,
`href="/favicon.ico?v=${version}"`
);
},
}],
};
Next.js (App Router):
// app/layout.tsx
import { readFileSync } from 'fs';
import { createHash } from 'crypto';
function getFaviconVersion() {
try {
const content = readFileSync('public/favicon.ico');
return createHash('md5').update(content).digest('hex').slice(0, 8);
} catch {
return '1';
}
}
export default function RootLayout({ children }) {
const v = getFaviconVersion();
return (
<html>
<head>
<link rel="icon" href={`/favicon.ico?v=${v}`} sizes="any" />
<link rel="icon" href={`/favicon.svg?v=${v}`} type="image/svg+xml" />
</head>
<body>{children}</body>
</html>
);
}
HTTP cache headers for favicons
Set short cache lifetimes for favicons that change, or use immutable for hashed filenames:
nginx:
# For /favicon.ico — allow some caching but not too long:
location = /favicon.ico {
expires 1d;
add_header Cache-Control "public, max-age=86400";
access_log off;
}
# For hashed filename (favicon.a3f8c2d.ico) — cache forever:
location ~* \.ico$ {
expires 1y;
add_header Cache-Control "public, max-age=31536000, immutable";
access_log off;
}
Express:
import express from 'express';
import { serveStatic } from 'express';
const app = express();
// Short cache for favicon.ico (1 day):
app.get('/favicon.ico', (req, res) => {
res.set('Cache-Control', 'public, max-age=86400');
res.sendFile('public/favicon.ico', { root: process.cwd() });
});
Vercel (vercel.json):
{
"headers": [
{
"source": "/favicon.ico",
"headers": [
{
"key": "Cache-Control",
"value": "public, max-age=86400, stale-while-revalidate=604800"
}
]
}
]
}
Force favicon reload in the browser (development)
During development, force a favicon refresh without clearing the entire cache:
// Paste in browser console to force favicon reload:
(function() {
const link = document.querySelector("link[rel*='icon']") || document.createElement('link');
link.type = 'image/x-icon';
link.rel = 'shortcut icon';
link.href = '/favicon.ico?' + Date.now();
document.head.appendChild(link);
})();
Or in Chrome: open DevTools → Application → Storage → Clear storage → check “Cache storage” → click “Clear site data.”
SVG favicon: no caching issues
SVG favicons accept <style> and currentColor, and because they’re often inlined or served with proper headers by modern build tools, they update more reliably:
<!-- favicon.svg — embed in HTML for instant updates: -->
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<rect width="32" height="32" rx="6" fill="#3b82f6"/>
<text x="16" y="22" font-size="18" text-anchor="middle" fill="white" font-family="sans-serif">X</text>
</svg>
<!-- Inline SVG as data URI — never cached separately: -->
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'><rect width='32' height='32' rx='6' fill='%233b82f6'/><text x='16' y='22' font-size='18' text-anchor='middle' fill='white'>X</text></svg>">
An inline data URI favicon is embedded in the HTML and caches with the page — if your HTML is cache-busted on deploy, so is the favicon.
Related tools
- Favicon Generator — generate ICO, PNG, SVG favicons
- HTML Minifier — minify HTML before deployment
- Image Compressor — compress favicon PNG files
Related posts
- Apple Touch Icon — Add an iOS Home Screen Icon to Your Website — The apple-touch-icon is the image iOS uses when someone adds your site to their …
- Favicon ICO — Create and Implement .ico Files for Your Website — The favicon.ico file is the icon shown in browser tabs, bookmarks, and history. …
- Favicon Size Guide — All the Sizes You Need for Every Browser and Device — Favicons require multiple sizes for browsers, mobile home screens, and bookmarks…
- PWA Icons — Web App Manifest Icons, Maskable Icons, and Apple Touch Icons — Progressive Web Apps need icons in multiple sizes for home screens, app switcher…
Related tool
Generate favicon package from any image or emoji. Multiple sizes (16, 32, 48, 180, 192, 512), manifest.json, and HTML snippet.
Written by Mian Ali Khalid. Part of the Frontend & Design pillar.