Favicon generators cheerfully hand you a zip of thirty-eight PNGs and a browserconfig.xml for Windows 8 tiles. Almost all of it is dead weight in 2026. What a current site needs is this:
| File | Why it exists |
|---|---|
/favicon.ico | A container bundling 16, 32 and 48 px. Browsers request it from the site root whether you link it or not, and it is what shows in bookmarks and older tooling. |
icon.svg | Vector, one file, sharp at any size. Modern browsers prefer it when offered. |
apple-touch-icon.png | 180×180, opaque background. iOS home-screen shortcuts ignore the manifest and composite transparency onto black. |
icon-192.png, icon-512.png | Referenced from the web app manifest for installed PWAs and Android home screens. |
The head markup is four lines:
<link rel="icon" href="/favicon.ico" sizes="32x32">
<link rel="icon" href="/icon.svg" type="image/svg+xml">
<link rel="apple-touch-icon" href="/apple-touch-icon.png">
<link rel="manifest" href="/manifest.webmanifest">
Start from a square PNG at 512 or 1024 px. ImageMagick 7 packs multiple resolutions into a single ICO in one pass:
magick icon.png -define icon:auto-resize=48,32,16 favicon.ico
magick icon.png -resize 180x180 -background white -alpha remove apple-touch-icon.png
magick icon.png -resize 192x192 icon-192.png
magick icon.png -resize 512x512 icon-512.png
On ImageMagick 6 the command is convert rather than magick. On macOS with nothing installed, sips -z 180 180 icon.png --out apple-touch-icon.png handles the PNG resizes, though sips cannot write multi-size ICO files. A finished favicon.ico with three sizes lands around 5-15 KB.
At 16×16 a wordmark is a grey smudge. Take the single most distinctive element — one letter, one glyph, one silhouette — and drop everything else. Two practical rules: keep strokes at least 2 px wide at the 16 px rendering, and leave roughly 10% padding inside the square so the mark does not collide with the tab's rounded corners. Then actually check it: shrink the PNG to 16 px in an image viewer at 100% zoom before you ship, because a design that reads at 512 frequently does not survive the downsample.
You can skip the file entirely and inline the icon:
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'
viewBox='0 0 100 100'><circle cx='50' cy='50' r='48' fill='%23ff5f1f'/></svg>">
This saves a request and is genuinely useful for prototypes and single-file pages. Caveats: you must percent-encode # as %23 or the URL truncates at your first hex colour, use single quotes inside the attribute, and keep the payload small — a couple of kilobytes is fine, a traced logo of tens of kilobytes bloats every page that carries it. Data-URI favicons that lean on a text glyph render from the local font stack, so the same markup looks different on Windows, macOS and Android — draw shapes instead if consistency matters.
A dark navy monogram vanishes against a dark tab strip. Because an SVG favicon is a document, media queries inside it work:
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<style>
path { fill: #14181f }
@media (prefers-color-scheme: dark) { path { fill: #f2f4f8 } }
</style>
<path d="M6 26 16 5l10 21z"/>
</svg>
The query resolves against the browser chrome's theme, not the page's. The ICO fallback cannot do this, so choose a mid-tone colour there that survives both backgrounds.
Favicons are cached harder than any other asset — browsers keep them in a separate store that a normal reload does not touch, and it is routine for an old icon to persist for days. The reliable fix is a new URL. Serve the icon from a versioned path such as /icon.svg?v=3 in the <link> tag and bump the number on every change. Note that the root /favicon.ico request is made without consulting your HTML, so it cannot be versioned — set a shorter Cache-Control: max-age=86400 on that one path and a long max-age on everything else. To verify locally, open the icon URL directly in a tab and hard-reload it there; that clears the entry the tab strip reads from.