Why your favicon doesn't show in Google Search

Kamalpannu1 pts0 comments

Why Your Favicon Doesn't Show in Google Search (Complete Next.js Fix)

Why your favicon doesn't show in Google Search — and everything else that silently broke after my Next.js deploy

By Preet · Solo developer, Vancouver · Debugged on my production app, jobcost.app

My deploy was green. The site worked. The favicon showed in the browser tab. Done, right?

Then over the next weeks I discovered, one by one: Google showed a gray globe instead of my logo, my link previews rendered differently on every platform (broken image on Facebook and Discord, fine on Twitter), and Safari on iPhone was showing the Vercel triangle in my tab strip — a logo I never put anywhere.

None of these threw an error. Nothing failed in CI. Everything just silently didn't work. Here is every cause I found, in the order you should check them, with the exact Next.js (App Router) fix for each.

1. Google ignores favicons that aren't a 48px multiple

✗ 16×16 .ico — ignored

✓ 96×96 png — shown

Illustration: same site, same deploy — the only difference is whether the icon meets Google's 48px-multiple rule.

Google requires the search-result favicon to be a square, and a multiple of 48px — 48×48, 96×96, 144×144 — or an SVG. The classic 16×16/32×32 favicon.ico renders fine in the browser tab and gets silently ignored by Google. The tab tells you nothing.

App Router fix — use the file convention, no manual tags:

app/<br>icon.png // square PNG, 96x96 or larger — Next.js generates the tag<br>apple-icon.png // 180x180 — you need this one more than you think (see #2)<br>favicon.ico // keep for legacy browsers — but read #2 first

2. The Vercel triangle you never added: create-next-app's default favicon

This one hurt. Safari on my iPhone showed the Vercel logo in the tab strip for my own product. Clearing history did nothing.

Cause: create-next-app ships a default favicon.ico (the Vercel/Next triangle). Depending on your version it lives in app/ or public/ — and if you replaced one location, the template file can survive in the other. public/favicon.ico will happily keep serving the triangle after you've added a beautiful app/icon.png.

my-app/<br>app/<br>icon.png ← you added ✓<br>apple-icon.png ← you added ✓<br>public/<br>favicon.ico ← template survivor ✗

what Safari shows

Illustration: the template public/favicon.ico outlives your new icons — and wins the route.

Three-part fix:

Search your repo for every favicon.ico — check public/ specifically. Delete the template one; keep exactly one, yours, in app/favicon.ico.

Add app/apple-icon.png (180×180). Safari prefers the apple touch icon for tab strips and bookmarks; without it, it falls back to that stale .ico.

Know that Safari caches favicons separately from history. "Clear History" does not clear it. Settings → Safari → Advanced → Website Data → delete your site's entry. And tabs already open keep the icon from when they loaded — test with a fresh tab, or you'll chase a ghost like I did.

Verify like an engineer, not a browser-refresher: open yourdomain.com/favicon.ico and /apple-icon.png directly in a private tab. What those URLs serve is the truth; everything else is cache.

3. You have twitter:image but no og:image

My link previews were inconsistent: fine on X, broken image everywhere else. Cause: my head had twitter:image but no og:image . X reads its own tag; Facebook, WhatsApp, LinkedIn, Discord, Telegram, iMessage all want og:image. Miss it and every platform except X shows a broken card.

head: twitter:image ✓   og:image ✗

image renders<br>X (Twitter) ✓

image not found<br>Facebook / WhatsApp ✗

image not found<br>Discord / LinkedIn ✗

Illustration: X reads its own tag; everyone else wants og:image. One missing tag, five broken platforms.

The Facebook Sharing Debugger even warns you — "Inferred Property: the og:image property should be explicitly provided" — but only if you think to look.

Related trap: if you have both old manual tags (from a Head component or next-seo) and the new metadata export, they fight — I ended up with og:image content="" rendering empty. One source of truth only. Delete every manual OG tag and use the export:

// app/layout.tsx<br>export const metadata: Metadata = {<br>metadataBase: new URL("https://yourdomain.com"), // required to resolve relative image paths<br>openGraph: {<br>title: "Your Title",<br>description: "Your description.",<br>url: "https://yourdomain.com",<br>type: "website",<br>images: [{ url: "/og-image.png", width: 1200, height: 630 }],<br>},<br>twitter: { card: "summary_large_image", images: ["/og-image.png"] },<br>};<br>Then verify in view-source: — exactly one og:image tag, absolute URL inside. Only after the raw HTML is right do the checkers mean anything.

4. Your fix is deployed — but your CDN is serving the old version

This is the one that cost me the most hours: I had already fixed the meta tags, but Cloudflare kept serving the cached old HTML and images. Every checker kept "proving" my fix didn't work.

If you're behind Cloudflare (or any CDN): after deploying meta/icon changes, purge the...

image favicon icon next google twitter

Related Articles