Your CDN’s Edge Caching Is Breaking Hreflang for International Visitors
On this page
- The mechanism: a location-blind cache key
- Why this specifically breaks hreflang
- Fix the cache key: vary by country
- Fix the redirects: hreflang URLs must not geo-redirect
- Verify what the crawler actually sees
- Why the cache layer, not the markup, is usually the culprit
- A practical order of operations
- Sources
- Related posts:
When geo-IP detection or geo-redirects run at your origin behind a location-unaware CDN, two failures stack. The edge caches one country’s response (or one geo-redirect outcome) under the URL alone and serves it to everyone who hits that node, and, the part that actually breaks hreflang, if an hreflang-declared URL itself geo-redirects, Googlebot crawling from US IP space gets bounced off the localized content it was told to verify. The declared French page redirects the crawler to the English page, so the declaration is unverifiable and the cluster falls apart. The fix is two-part: vary the cache key by country, and make every hreflang-declared URL serve its declared content to all requesters, crawlers included.
The mechanism: a location-blind cache key
A CDN edge decides what to serve by computing a cache key. By default that key is essentially the URL. If your origin inspects the visitor’s IP and returns a country-specific body, or issues a geo-redirect, but the CDN keys only on the URL, then the first request to reach a given edge node populates the cache, and every later request for that URL at that node receives the same stored response regardless of where it came from. A visitor in Paris can be served the snapshot a visitor in Chicago triggered moments earlier. The variance you intended (right locale to right market) collapses into whichever locale happened to fill the cache first.
Why this specifically breaks hreflang
Hreflang is a set of mutual declarations: each localized URL names its alternates, and Google verifies the cluster by crawling those URLs. Googlebot crawls primarily from US IP ranges and relies on the hreflang annotations rather than spoofing a French or German location to discover localized content. So if your /fr-fr/ URL geo-redirects US-based requesters to /en-us/, Googlebot following the French alternate never reaches French content. From Google’s side the declared French version is missing or contradicts itself, and the reciprocal cluster cannot be confirmed. The localized pages can still each be indexed individually, but the cross-linking that puts the right version in front of the right market does not hold.
This is why a clean validation can coexist with a broken cluster. Search Console no longer surfaces a dedicated hreflang or “International Targeting” report (that report was deprecated), so an absence of flagged errors tells you nothing. The redirect fires at crawl time, and you only catch it by inspecting what Googlebot actually receives.
Fix the cache key: vary by country
The cache must stop collapsing distinct-locale responses into one entry. On Cloudflare this is a Cache Rule with a custom cache key that includes the visitor’s country (the geo option derives country from the client IP), so each country gets its own cache entry instead of inheriting another country’s stored response. The equivalent on other stacks is keying on country through Workers or edge logic, or emitting an appropriate Vary response so the cache stores per-segment variants. Accept the trade: sharding the cache by country lowers the hit ratio per segment, because you now store more variants. That is the cost of serving the correct locale, and it is the correct cost to pay.
Fix the redirects: hreflang URLs must not geo-redirect
The deeper fix is structural. Every URL that appears in an hreflang annotation must serve its declared language and country content to any requester, regardless of where the request originates. Disable geo-redirects on those specific URLs. You can keep geo-detection as a convenience elsewhere (for example, suggesting a locale on the homepage), but the canonical localized URLs themselves stay stable.
Serving Googlebot the same content a target-country user would see on that URL is not cloaking. Google’s locale-adaptive guidance treats geo-IP adaptation for users as acceptable as long as the declared URL returns its declared content to the crawler; the deception line is crossed only when you show the crawler something materially different from what the intended audience gets. Consistent content for the crawler plus geo-IP convenience for users, applied carefully, stays on the right side of that line.
Verify what the crawler actually sees
Do not trust the absence of a Search Console flag, validate the fetch.
- URL-Inspect each localized URL in Search Console and confirm the fetched content is the declared language and country, not a redirect target.
- Spot-check from multiple regions using a VPN, requesting the hreflang URLs directly, and confirm each returns its own localized content rather than a geo-redirect to your default locale.
- Crawl the localized URL set with a third-party crawler (Screaming Frog, Sitebulb) to confirm reciprocal hreflang annotations resolve to live, non-redirecting URLs, since Search Console no longer reports hreflang validation itself.
- Audit where geo-detection runs. If it executes at the origin behind a URL-only cache key, you have both failure modes at once and must fix the cache and the redirects together.
The two root causes hide each other. A team that “set up hreflang correctly” in the markup can still ship a location-blind cache key and a crawl-time geo-redirect, and the annotations will look perfect while the cluster quietly fails. Separate the declaration (correct in the HTML) from the delivery (what the edge and the redirect actually return to a US-based crawler), and fix the delivery.
Why the cache layer, not the markup, is usually the culprit
Most teams investigating a broken hreflang cluster start in the HTML, re-checking that every alternate names every other alternate and that the return tags are reciprocal. That work is necessary but rarely sufficient, because the markup is static and the failure is dynamic: it happens at request time, at the edge, for a specific class of requester. The annotation can be flawless in your editor and in the page you load from your office, and still resolve to the wrong body for a crawler in US data-center IP space hitting a cold edge node. This is why the symptom is so confusing. It is intermittent by geography and cache state, it does not reproduce when you test from the target country, and it leaves no static trace to grep for.
The mental model that fixes it is to stop thinking of an hreflang URL as “a page” and start thinking of it as “a contract to return specific content to anyone who asks.” A geo-redirect on that URL voids the contract for out-of-region requesters, and Googlebot is permanently out of region. A country-blind cache key voids it for whoever did not happen to warm the cache. Both are delivery decisions made below the markup, which is why no amount of annotation tidying repairs them.
A practical order of operations
When you inherit this problem, work it in this sequence rather than all at once. First, confirm the symptom with URL Inspection on two or three localized URLs and note whether the fetched content matches the declaration or shows a redirect. Second, trace where the geo-logic runs by reading the response for a localized URL with an out-of-region request and watching for a 3xx status or a swapped body. Third, remove geo-redirects from the hreflang-declared URLs specifically, leaving any homepage locale suggestion intact. Fourth, add the country dimension to the cache key so distinct locales stop overwriting one another at the edge. Fifth, re-inspect and re-crawl to confirm each declared URL now returns its declared content to a crawler-style request. Fixing the redirect without the cache key, or the reverse, leaves you half-broken and chasing an inconsistent symptom, so treat them as one change shipped together.
Sources
- Google Search Central: “Managing multi-regional and multilingual sites”: https://developers.google.com/search/docs/specialty/international/managing-multi-regional-sites
- Cloudflare Cache docs: “Custom cache keys” (Cache Rules): https://developers.cloudflare.com/cache/how-to/cache-rules/settings/
- Google Search Central: “Localized versions of your pages” (hreflang): https://developers.google.com/search/docs/specialty/international/localized-versions