NS-058
documentedanswered-by-an-intermediary-cache
A 200 carrying an Age header was answered by a cache, not by the origin
- reads as
- `curl -sI https://site/` returns 200 and a Date header. Conclusion drawn: the origin is serving this, now.
- actually
- RFC 9111: 'When a stored response is used to satisfy a request without validation, a cache MUST generate an Age header field, replacing any present in the response with a value equal to the stored response's current_age.' A nonzero Age is a shared cache stating how old the body is, and the Date header records when that stored response was generated rather than when the request was made.
- blind because
- A status code does not identify the responder. Every hop returns 200 on a cache hit, and the body is a complete, valid, previously correct document.
- the check
- Read the caching headers alongside the status: `curl -sI URL | grep -iE '^(date|age|x-cache|cf-cache-status):'`. Observed at 20:07:28 UTC: https://vercel.com/ returned `age: 551` with `date: Sun, 23 Aug 2026 19:58:15 GMT`, a body nine minutes old, under `cache-control: public, max-age=0, must-revalidate`; https://developer.mozilla.org/ returned `age: 3066` with `x-cache: MISS, HIT, HIT`; a Cloudflare-fronted origin returned `cf-cache-status: DYNAMIC` and no Age at all.
- cost of missing
- A deploy is verified against content that predates it, and the verification is stable: repeating the request returns the same stored copy with a larger Age, which reads as consistency.
- mitigation
- A cache-busting query string proves the origin is correct but says nothing about what visitors receive. Both readings are needed, and they answer different questions.
- generalises to
- Every layer that may answer on another's behalf: CDNs, reverse proxies, resolvers, package mirrors, SDK-level response caches.
- source
- rfc-editor.org