WriteMySEO / Blog / CDN Cache Keys Decide Which Page Googlebot Sees
SEO technology

CDN Cache Keys Decide Which Page Googlebot Sees

Your edge cache chooses the HTML Googlebot receives. Here's how cache keys, Vary headers and personalization cause wrong-variant indexing, and how to diagnose it.

Most teams treat the CDN as a speed layer: same HTML, delivered faster. It isn't. A CDN is a decision layer that picks which stored response to hand back, and the rule it uses — the cache key — is usually configured by someone optimizing for hit rate, not for what Googlebot indexes.

When the cache key is narrower than the set of responses your origin can produce, the edge will happily serve one variant to everyone. Sometimes the variant Googlebot gets is a geo-redirect page, a logged-out shell, a bot-challenge interstitial, or last week's HTML. None of that shows up in a normal browser test, because your browser gets a different cached object than the crawler does.

What a cache key actually is

A cache key is the string a CDN builds from the request to look up a stored response. By default on most CDNs it's roughly:

scheme + host + path + (query string, sometimes normalized or stripped)

Notice what's not in there: request headers, cookies, client geography, user agent, Accept-Language. If your origin varies the HTML on any of those inputs but they aren't part of the key, the cache is storing one response for many logically distinct requests. First request wins; everyone else gets that copy until it expires.

The HTTP-standard fix is the Vary response header, which tells shared caches "this response depends on these request headers, store variants separately." Most CDNs honor Vary for a limited set of headers, and many let you add key components directly in configuration instead:

# Fastly VCL sketch
sub vcl_hash {
  hash_data(req.url);
  hash_data(req.http.host);
  hash_data(req.http.X-Device-Class);   # normalized to mobile|desktop at the edge
  hash_data(req.http.X-Geo-Country);
}

The practical rule: every input your origin uses to change the HTML must appear either in Vary or in the CDN's cache key. If it appears in neither, you have a variant-leak.

The four failure modes that change what gets indexed

1. Geo-personalized HTML under a shared key. Google has documented that Googlebot crawls primarily from US-based IP addresses, with some locale-distributed crawling. If your origin swaps currency, language, or the whole page based on IP, and country isn't in the cache key, then whichever variant got cached first is what the crawler receives — and whichever variant the crawler receives can be what gets indexed for every market. The nastier version is a 302 to /us/ cached under the bare path: now your canonical landing page is a redirect in Google's eyes.

2. Cookie- or session-dependent HTML. Googlebot generally does not carry cookies across requests, so it always looks like a brand-new anonymous visitor. If your origin renders a different shell for anonymous users — a consent wall, a "choose your store" screen, an empty personalized module — that's the version in the index. If instead a logged-in response gets cached under a key that ignores the session cookie, the crawler can receive account UI and, occasionally, private content. That's a security bug before it's an SEO bug.

3. Mobile and desktop HTML under one key. Google indexes with the smartphone Googlebot. If you use dynamic serving — different HTML from the same URL depending on user agent — and the cache key doesn't distinguish device classes, Googlebot can be served the desktop variant. Google's dynamic-serving guidance has long recommended Vary: User-Agent for exactly this reason.

4. Cached error and challenge pages. Bot-mitigation interstitials, rate-limit pages, and maintenance notices are often returned with a 200 status and short bodies. If the edge caches one of those under the normal key, every subsequent crawl gets it. A cached 200 containing a challenge page is a strong candidate for Google to treat as a soft 404, and it will stay that way until TTL expiry or a purge.

Why Vary: User-Agent is a blunt instrument

Vary: User-Agent is correct HTTP, but user agent strings are nearly unique per browser build. Telling a shared cache to key on the full UA string fragments your cache into thousands of near-identical objects and collapses hit rate — the exact outcome the CDN was bought to prevent.

The better pattern is to normalize at the edge: parse the UA once, reduce it to a small enumerated value (mobile, desktop, bot), set that as a custom header, and put that in the cache key. You get correct variant separation with a handful of objects per URL instead of thousands. If you must emit Vary: User-Agent for downstream proxies you don't control, pair it with an edge-level normalized key so your own CDN stays efficient.

And the simplest option is often best: if you can serve one responsive HTML document to everyone, you delete the entire class of problem.

How to find out which variant Google actually got

Don't reason about this from config. Fetch and diff.

  1. Compare the rendered HTML in Search Console's URL Inspection tool against what you get in a browser. The "view crawled page" HTML is the single most direct evidence of what Google received.
  2. Diff origin against edge. Request the URL with the CDN bypassed (direct to origin, host header set) and through the CDN. Differences are cache-layer artifacts by definition.
  3. Vary one input at a time. Same URL, different Accept-Language; same URL, mobile vs desktop UA; same URL from a non-US exit node. If the body changes, that input must be in the key.
  4. Read the cache headers on every response. They tell you whether you're looking at a fresh object or a stored one.
HeaderWhat it tells you
x-cache / cf-cache-statusHIT, MISS, EXPIRED, BYPASS — vendor-specific but decisive
ageSeconds this object has been sitting at the edge
cache-control (s-maxage)How long shared caches may keep it, separate from browser TTL
varyWhich request headers the origin claims matter
set-cookieA response that sets cookies should almost never be shared-cached

One caution: testing your own site with a Googlebot user agent string is fine, but many origins verify crawlers by reverse DNS, so a spoofed UA may not reproduce the path Googlebot actually takes. Where verification exists, test both with and without it.

Cache TTL is part of your time-to-index

Edge TTL stacks on top of crawl interval. If HTML sits at the edge with s-maxage=86400 and Googlebot crawls the URL a few hours after a publish, it gets yesterday's document — and the freshly updated version may not be fetched again for days. Revalidation with ETag or Last-Modified reduces bandwidth but doesn't help if the edge considers the object fresh and never asks the origin at all.

The related deploy bug: purging static assets but not HTML. Googlebot then renders stale HTML that references hashed JS and CSS filenames that no longer exist, producing resource 404s during rendering and, in bad cases, a page that renders empty.

What to do

cdncachingcrawlinghttp headers

WriteMySEO produces marketing content, not legal, medical, financial, or compliance advice. Figures cited reflect publicly reported industry data at time of writing and shift over time.

Get started

We write this well about your industry, every month.

AI-drafted, human-reviewed SEO content on a flat subscription. Blog posts, metadata, schema, and internal links, shipped on a monthly rhythm.

See plans

More from the blog