Why doesn’t AI mention your brand? Find out which half is brokenGet a free report
LLMention
Method

Cache headers and what an auditor actually sees

An external audit read the old page after a deploy because the headers allowed it. The fix was not a harder purge; it was to stop granting permission to be stale.

By Alex Xu9 min read

The page you see and the page an auditor sees are not necessarily the same response. Between the origin and the auditor sit a browser cache, a CDN, a framework cache, and sometimes the audit tool’s own storage. Each one is allowed to keep a copy for as long as the cache headers say. When the headers say "a day", a deploy does not change the copies that already exist.

This site learned that the hard way on 2026-10-23: the origin was updated, and an external audit read an old version because the HTML headers permitted intermediaries to serve a stale copy. This article is about what those headers mean, what an auditor actually receives, and why the fix was to make HTML revalidate rather than to purge harder.

The layers between you and the reader

LayerWhat it holdsWhat controls it
Browser cacheA copy on the reader’s device.Cache-Control (the max-age part).
CDN / edge cacheA copy at the nearest point of presence.s-maxage, stale-while-revalidate, CDN rules.
Framework cacheA prerendered page held by the app runtime.Framework config and the response headers it emits.
Audit toolIts own snapshot of what it fetched.Nothing you can set; it may not refetch on demand.

Only the first three are influenced by your headers, and the third-party tool is the one that reports what it saw. That asymmetry is the trap: you can verify the origin and still fail to verify the experience.

The directives that matter

  • max-age=N — a browser may reuse the response for N seconds without asking.
  • s-maxage=N — a shared cache (CDN) may reuse it for N seconds. This is the directive that produces "the deploy is live but the edge still serves the old page".
  • stale-while-revalidate=N — a cache may serve the stale copy for N seconds while it revalidates in the background. It is a performance feature and a permission to be out of date.
  • no-store — do not store the response at all. Correct for per-request pages and APIs.
  • must-revalidate — once stale, the cache must check with the origin rather than serving the old copy.
  • ETag / 304 — the cheap revalidation: a request comes in, the origin says "unchanged", and the body is not resent.

What this site had, and what it changed

Before the fix, content HTML carried s-maxage=86400 and stale-while-revalidate=604800. In plain terms: a shared cache could serve a copy for a day, and keep serving a stale copy for up to a week while revalidating. For a site whose product argument is "the published version is the running version", that is the wrong trade. An external audit read a version that no longer matched the origin, and the report was written against the old page.

The fix was to stop granting the permission. HTML now carries public, max-age=0, must-revalidate, and the ETag makes the revalidation a 304 rather than a full response. stale-while-revalidate is gone from HTML entirely. Static assets under /_next/static/ keep a long cache, which is correct because their filenames are content-hashed: a new build produces new URLs, so an old copy can never be the wrong version of a current URL.

Per-request pages are the other direction: /report/ is generated from the visitor’s own domain and /api/ returns live data, so both are no-store. Caching one visitor’s scan and serving it to the next would be a correctness bug, not a performance trade.

What an auditor actually sees

"I deployed it" answers a question about the origin. The auditor’s question is "what did you receive, and when". The useful evidence is the response they got: status, Cache-Control, Age, ETag, and the content itself. A response with a large Age and an old title is an old copy, regardless of what the origin has now.

# 1. What headers does the deployed page return?
curl -sS -D - -o /dev/null https://your-domain.com/

# 2. Fetch twice and compare Age / ETag
curl -sS -D - -o /dev/null https://your-domain.com/ | grep -iE 'cache-control|age|etag|cf-cache-status'

# 3. Does the body contain the version marker you expect?
curl -sS https://your-domain.com/ | grep -oE '<title>[^<]+'

The second command is the one that distinguishes a cache hit from a fresh response. If an intermediary reports HIT with a growing Age, the response did not come from the origin; the origin’s state is not what the reader saw.

The tests that keep it fixed

A header policy that is only written down drifts. This repository asserts the cache header incheck:live, so a deploy that accidentally restores a long s-maxage on HTML fails the smoke check rather than waiting for an external report. The deploy also purges the edge on a best-effort basis, because a header change and a deploy can race with a copy that was already cached.

The general lesson is worth stating separately from this site: tests that fetch the origin are not tests of what a reader receives. If the product claims a version is live, the test has to look at the response the way an outsider would, including the cache headers.

The trade, stated plainly

Revalidating HTML costs a round trip per view. Keeping static assets immutable costs nothing because their URLs change with the content. Caching per-request pages would be a correctness bug. The choice is not "performance versus correctness"; it is deciding which content can tolerate being old and for how long, and writing that decision into the headers instead of hoping the intermediary agrees.

For a site that publishes a number, a score, or a date, "old" is a wrong answer. That is the whole reason the header changed.

Questions about cache headers

Why did an audit see the old page after a deploy?

Because a cache between the origin and the auditor was allowed to keep serving the old response. `s-maxage` and `stale-while-revalidate` are permission to do exactly that; the deploy changed the origin, not the copies already held by intermediaries.

Is max-age=0 bad for performance?

It costs a revalidation request, not a full download: with an ETag the answer is usually a 304. For HTML on a site whose accuracy is the product, that round trip is the correct trade. Static assets with content-hashed filenames keep their long cache.

Does purging the CDN fix it?

It helps, and it is best-effort: you cannot purge caches you do not control, including a reader's browser or a third-party audit tool's own cache. The durable fix is the header, so the copy is not allowed to age in the first place.

Evidence and sources

GEO can boost visibility by up to 40% overall in generative engine responses, and in the paper's Table 1 the best of the tested methods improve on the no-optimization baseline by 41% and 28%. — Generative Engine Optimization, KDD 2024

Every figure in this piece comes from the published study or from the rule set the scanner executes; both are linked, and both can be re-run.

Primary sources

Alex Xu — Maintainer, LLMention

I build and maintain LLMention: the scanner, the rule set and the study behind it. The work is published as source, so the method can be checked rather than taken on trust.

How this site is written and funded →

Read next