Why your favicon doesn't show in Google Search — and everything else that silently broke after my Next.js deploy
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
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/
icon.png // square PNG, 96x96 or larger — Next.js generates the <link> tag
apple-icon.png // 180x180 — you need this one more than you think (see #2)
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.
public/favicon.ico outlives your new icons — and wins the route.Three-part fix:
- Search your repo for every
favicon.ico— checkpublic/specifically. Delete the template one; keep exactly one, yours, inapp/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.
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 <meta> 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
export const metadata: Metadata = {
metadataBase: new URL("https://yourdomain.com"), // required to resolve relative image paths
openGraph: {
title: "Your Title",
description: "Your description.",
url: "https://yourdomain.com",
type: "website",
images: [{ url: "/og-image.png", width: 1200, height: 630 }],
},
twitter: { card: "summary_large_image", images: ["/og-image.png"] },
};
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 cache for the affected paths (or purge everything). Then — separately — make Facebook re-fetch: Sharing Debugger → your URL → Scrape Again. Facebook holds its own cache of your old broken card and will show it for weeks otherwise.
Debugging order that saves your sanity: raw view-source → direct asset URLs in a private tab → purge CDN → re-scrape checkers. Truth flows in that direction.
5. robots.txt is silently blocking your icon
Google fetches your favicon with a crawler. If robots.txt blocks the icon's path — or a catch-all rule catches the /_next/ asset path — Google gives up with no warning anywhere. Check yourdomain.com/robots.txt in production and confirm what app/robots.ts actually outputs.
6. Your homepage isn't indexed yet
Google only shows a favicon for sites whose homepage it has indexed. Fresh domain, fresh deploy = no index = gray globe regardless of a perfect setup. Search Console → URL Inspection → homepage → Request Indexing.
7. Everything is right — Google is just slow
Even with all of the above correct, Google can take days to weeks to fetch and display a new favicon, and there is no "refresh favicon" button. Requesting reindexing nudges it; after that it's a queue. Sometimes the gray globe isn't a bug — it's a waiting room.
The full post-deploy checklist
- Favicon: square, 48px-multiple, stable URL, not robots-blocked
- Template
favicon.icodeleted frompublic/ANDapp/— yours in exactly one place apple-icon.png180×180 existsog:imagepresent (not just twitter:image), absolute URL, 1200×630, loads with 200- Exactly one set of OG tags in view-source — no duplicates fighting
- CDN purged after every meta/icon change; Facebook re-scraped
- Sitemap present and submitted; robots.txt allows what you think
- Canonical points at the right domain; homepage indexing requested
I'm building a tool that checks all of this in one shot
Paste your URL, get every silent post-deploy failure flagged with the exact Next.js fix — every check in this article, automated. Free when it launches.
Written by a dev who stared at a gray globe, a broken preview card, and a Vercel triangle he never asked for. If this saved you that time, the email signup genuinely helps me.