Field guide

Favicon AuditValidation

Favicon Audit Field Guide: Catching the Tab Icons, Touch Icons, and PWA Bits That Break Quietly

faviconseopwapre launch checklistvalidation
Senrok Team
BySenrok Team
Published

A favicon is a 16-by-16 pixel file that nobody notices until it is broken. Then it is the only thing anyone notices — a torn paper icon in a tab strip, a screenshot of the page instead of a logo on an iPhone home screen, a "Add to Home Screen" prompt that installs nothing.

The Favicon Audit is the smallest, fastest pre-launch check we run against a site. This guide is the operator's notes for it: what it actually checks, what it intentionally ignores, and how to fold it into a release flow without turning it into busywork.

What the audit checks

The audit fetches a URL through our hardened CORS proxy and parses the HTML for the six declarations that decide whether a browser, a search crawler, and a home-screen installer can all find the right icon:

  • /favicon.ico — the legacy path every browser falls back to when nothing else is declared
  • Apple touch icon — <link rel="apple-touch-icon"> or /apple-icon.png
  • Generic icon — <link rel="icon">, with a size at or above 32×32
  • Web app manifest — <link rel="manifest"> referenced and reachable
  • Theme color — <meta name="theme-color"> set
  • Viewport meta — <meta name="viewport"> set

Each row in the result table is one of: Pass, Missing, or Check. There is no scoring system and there are no weighted rules. The list is the list, and the goal is to read it as a checklist, not a grade.

What the audit does not check

A few things that look like favicon work but live elsewhere:

  • Image quality — whether the icon is actually well-designed, legible at 16×16, or on-brand. That is a design call, not a check.
  • CDN reachability — the proxy can confirm the link tag is present, but the icon itself is requested only via standard fetch with image MIME sniffing. If your CDN is geo-blocked, the audit will return a passing link-tag check even when real users in some regions cannot load the file.
  • Cross-browser rendering — we test in Chromium. Safari's iOS-specific behavior (rounded corners, glossy overlay) and Firefox's private-window icon cache are out of scope.
  • Manifest validity — we check that the manifest is referenced, not that the JSON inside is well-formed or has every required field. Use a separate manifest validator for that.

If a check passes, the link tag is present and reachable. If a check fails, the link tag is missing or the URL returns a non-image response. That is the entire contract.

When to run it

Three moments, in order of how often they catch real bugs:

  1. Before launch, on the production URL, after the last content template is locked. Favicon regressions almost always come from template changes, not from the icon assets themselves.
  2. After any template overhaul — a CMS upgrade, a design system migration, a font swap, or any change that touches <head> rendering.
  3. When diagnosing a specific complaint — a partner saying "the tab icon is broken on your site", a designer noticing the iOS home screen is wrong, or a sales engineer noticing the PWA install prompt doesn't carry the right identity.

Don't run it in CI on every commit. The proxy makes a network call, and the audit is not the right place to catch a regression that you can already see in code review. Save it for the moments above.

Reading the output

The result table has three columns: the check name, the path or selector it is looking for, and the status. A few things to keep in mind when you look at the table:

  • Pass on /favicon.ico matters even if you have a <link rel="icon"> declaration. Some crawlers and tab-restore flows in older browsers will only request /favicon.ico. Skipping it costs you nothing and saves you a 404 in server logs.
  • A "Check" status on the manifest link is fine if you are not shipping a PWA. The manifest is required for an installable PWA. If you don't have a PWA, the missing link is informational, not a fail.
  • A "Missing" on theme-color is rarely urgent on its own, but it changes how Chrome paints the address bar on Android, which is the only reason most users notice. If you have a brand color, declare it.

The audit is shallow by design. It will not catch every problem, and it will not explain why a problem happened. The job is to tell you whether a problem exists in the six declarations above, not to fix it for you.

Common failures and how to fix them

A short list of the failures we see most often, in the order we see them:

1. The CMS template strips the <link rel="apple-touch-icon"> line on a redesign. This shows up as a Missing on the apple-touch-icon check after a visual overhaul. Fix: add a 180×180 PNG to the public assets and a <link rel="apple-touch-icon" href="/apple-icon.png"> in the head. The CMS head template is the right place — not the page template.

2. The favicon is hosted on a third-party domain that has been retired. This shows up as a Missing on /favicon.ico even though the link tag is present. Fix: serve the favicon from the same origin as the page. The browser doesn't care about the URL, but crawlers and legacy code do.

3. The manifest is referenced but the file is 404. This shows up as a "Check" because the link tag is valid but the fetch fails. Fix: confirm the manifest path resolves to a real file. The audit can flag the link, but a manifest validator will tell you the contents are wrong.

4. Theme color is set in CSS but not in the meta tag. Theme color is read from the meta tag only, not from CSS variables. The audit will correctly flag it as Missing. Fix: add <meta name="theme-color" content="#hex"> to the head.

Adding it to a release flow

The cleanest way to use the audit is to make it one of the last three pre-launch steps, alongside a page-speed check and a content-snapshot diff. A simple flow:

  1. Open the audit on the staging URL.
  2. Confirm six green rows.
  3. If any row is red or amber, either fix it before the deploy or note it explicitly in the launch doc.
  4. Re-run the audit on the production URL after the deploy lands.
  5. Save the result in the launch channel.

That is the entire flow. The audit is not a CI gate, not a monitoring check, and not a periodic scan. It is a single confirmation that the six declarations are in place at the moment of launch.

When to graduate to a custom system

If you are running the audit more than a few times a week, you are probably at the point where a custom system makes more sense. A few signals that it is time:

  • You ship a PWA and need the audit to validate the manifest contents, not just its presence.
  • You have multiple environments (preview, staging, prod) and you want the audit to run on all of them in parallel with diff reporting.
  • You need to audit pages behind authentication, and the CORS proxy model does not work.
  • You have brand guidelines that need to be enforced automatically — wrong icon dimensions, wrong file format, wrong colors in the manifest theme.

In any of those cases, the right move is a custom tool that runs against your deployment pipeline, with whatever rules your brand and product need. The free audit is the right starting point, not the final word.


The audit is at /tools/favicon-audit. Run it on a staging URL before every launch. The proxy makes one fetch per audit; nothing is stored, and nothing is sent to a third party beyond the URL you submit.