I maintain a small collection of single-purpose web tools. Last month I built a favicon checker: you type a URL, it reads the icon declarations in the HTML head, probes every referenced file, and also hits /favicon.ico directly, because plenty of software still requests that path without ever reading your HTML.
The first thing you should do with any auditing tool is point it at your own stuff. So I did. Ten sites, all built by me, all shipped and verified in browsers I actually use.
All ten failed. Not seven out of ten. Ten.
Failure one: SVG-only icon sets
Every site had a nice crisp favicon.svg and nothing else. Modern browsers request it, render it at any size, everything looks great in Chrome and Firefox. Then something older comes along: a bookmark sidebar, an RSS reader, a corporate proxy portal that lists your link, that one intern running Opera 12. These clients do not parse your <link> tags. They request /favicon.ico and hope.
All ten sites returned a 404 for that path. The fix is not glamorous. You need an actual .ico file, ideally with 16, 32, and 48 pixel frames packed inside, plus a PNG for iOS. More on that below.
Failure two: no apple-touch-icon
Nine of the ten sites had no apple-touch-icon.png. When someone saves such a site to an iOS home screen, Safari does not use your favicon. It takes a screenshot of the page, letterboxes it, and calls that your app icon. If you have ever seen a bookmark that looked like a cropped text paragraph, that is why.
The fix is one file and one tag: a 180 by 180 PNG, referenced with <link rel="apple-touch-icon" href="/apple-touch-icon.png">. Done. No JavaScript, no media queries, no dark mode variants needed. iOS rounds the corners itself.
Failure three: the 404 that would not leave
This is the one that cost me an evening, so pay attention if any of your sites sit behind Cloudflare.
I generated the missing icons, deployed them, and re-ran the checker. Still 404. I deployed again. Still 404. I started doubting my build, my deploy script, my cache headers, my sanity.
The files were fine. The problem was that Cloudflare had cached the original 404 responses. The .ico extension is on Cloudflare’s default cached file list, and that rule does not distinguish between a 200 with an image and a 404 with an error page. The edge had helpfully stored my failure and now served it at speed.
You can confirm this with a response header check: if cf-cache-status: HIT shows up on a request you know should be fresh, you are looking at a cached answer, not the origin’s. Two ways out: request the file with a query string like ?v=2, which bypasses the cache and shows you what the origin actually returns, or wait for the cached entry to expire on its own. Purging works too if you have dashboard access.
So the deploy sequence that actually worked was: upload the files, verify with a query string, then trust that the plain path would heal itself as the cache aged out.
How I generated everything locally
No online favicon service, because the source icons belong to the sites and uploading them somewhere for conversion defeats the point. The pipeline:
- Render the source SVG in a headless browser with a transparent background, at each target size. This respects whatever the SVG actually draws, which matters for icons with effects that naive rasterizers mangle.
- Use Pillow to pack 16, 32, and 48 pixel frames into a single multi-resolution
.ico. - Save a 180 by 180 PNG as
apple-touch-icon.png. - Add the tag block to the HTML head of every page template.
Then the checklist before calling it done: /favicon.ico returns 200 with an image content type, apple-touch-icon.png exists, and the sizes declared in the HTML match the actual pixel dimensions of the files. That last one is sneaky. I found two sites declaring sizes="32x32" on a file that was actually 48 pixels square. Browsers mostly cope, but a checker that compares declared versus actual will flag it, and the fix is a one-line attribute edit.
The five minute version for your own sites
If you want to audit your own setup without building anything, run through this:
-
curl -I https://yoursite.example/favicon.icoand look for200plus an image content type, nottext/html - Check that
/apple-touch-icon.pngexists and is 180 by 180 - Compare the
sizesattributes in your head against the real dimensions of the files they point to - If you use a CDN, remember it may cache your 404s the same way it caches your assets
Or point a checker at it. I eventually wired the probe logic into a free favicon checker that runs the whole battery in one go: the direct /favicon.ico request, every declared icon, the apple-touch-icon, format sniffing, and declared versus actual dimensions. It runs server-side probes only, no account, and it is how the ten-versus-zero result above got measured. For a deeper reference on sizes, formats, and which tags to use, the favicon best practices guide on the same site covers the long version.
The humbling part of shipping an auditing tool is that it audits you first. All ten of my sites failed a check I had just written the logic for. The icons took an afternoon to fix. The Cloudflare cache lesson would have taken much longer to debug in production, and that one I only hit because the checker kept retesting instead of trusting my deploy script’s success message.
Tools that re-verify from the outside are worth more than tools that report what you intended.