Back to HTTP Headers

Cache-Status proxy response

Tells the client exactly what each cache in the chain did with the request — hit, miss, stale, expired, or bypassed.

What it does

Cache-Status tells the client exactly what every cache in the chain did with this request — hit, miss, stale, expired, or bypassed. Each intermediary cache appends its own status entry as the response travels back to the client.

It's the standardised replacement for the X-Cache header that CDNs have been using inconsistently for years. Cloudflare uses CF-Cache-Status: HIT, Fastly uses X-Cache: HIT, AWS CloudFront uses X-Cache: Hit from cloudfront — all doing the same thing with different names and formats. Cache-Status (RFC 9211, 2022) gives everyone a common format.

Syntax

Cache-Status: <cache-name>; <parameter>[; <parameter>]
Cache-Status: <cache1>; hit, <cache2>; fwd=miss

Each entry: a cache name (quoted string or token) followed by semicolon-separated parameters.

Examples:

Cache-Status: ExampleCDN; hit
Cache-Status: ExampleCDN; fwd=miss; stored
Cache-Status: ExampleCDN; hit; ttl=1800
Cache-Status: OriginCache; fwd=stale; fwd-status=200; stored, CDN; hit

Parameters

Parameter Meaning
hit Cache served the response from its store
fwd=<reason> Cache forwarded the request (didn't serve from cache)
fwd-status=<code> HTTP status of the forwarded response
stored Cache stored the response after forwarding
collapsed Request was collapsed with another in-flight request
ttl=<seconds> Time-to-live remaining when the response was served
key=<string> Cache key used (for debugging)
detail=<string> Implementation-specific detail string

fwd values (why the cache forwarded)

fwd value Meaning
miss Not in cache
stale In cache but stale, revalidated
request Request headers forced bypass (Cache-Control: no-cache)
bypass Cache is configured to bypass for this request
method Request method not cacheable
vary Vary mismatch — no matching stored variant
expired Cached copy expired

Reading a multi-cache response

Cache-Status: OriginShield; fwd=miss; stored, EdgeCache; hit; ttl=3540

Reading right to left (last cache in chain first... actually left to right — first entry is the last to add):

  • EdgeCache; hit; ttl=3540 — the edge CDN node served from cache, 3540 seconds TTL remaining
  • OriginShield; fwd=miss; stored — the origin shield had a miss, fetched from origin, stored it

This tells you: edge cache HIT, origin shield was initially a MISS (cold cache or first request after TTL expired).

X-Cache vs Cache-Status

X-Cache is the old de facto standard — every CDN implemented it differently:

# Cloudflare
CF-Cache-Status: HIT

# Fastly
X-Cache: HIT
X-Cache-Hits: 3

# CloudFront
X-Cache: Hit from cloudfront

# Varnish
X-Cache: HIT
X-Varnish: 123456789 123456780

Cache-Status gives a unified format. Adoption is growing but X-Cache variants will persist for years. In 2026, check for both when debugging CDN caching issues.

Debugging with Cache-Status

# Check cache status on a response
curl -sI https://example.com/api/data | grep -i 'cache-status\|x-cache\|cf-cache\|age'

# Typical hit response:
# Cache-Status: Fastly; hit; ttl=3598
# Age: 2

# Typical miss response:
# Cache-Status: Fastly; fwd=miss; stored
# Age: 0

Real-world examples

Simple CDN hit:

HTTP/1.1 200 OK
Cache-Status: MyCDN; hit; ttl=86398
Age: 2
Cache-Control: public, max-age=86400

Miss that got stored:

HTTP/1.1 200 OK
Cache-Status: MyCDN; fwd=miss; stored
Age: 0
Cache-Control: public, max-age=3600

Stale response served while revalidating:

HTTP/1.1 200 OK
Cache-Status: MyCDN; hit; fwd=stale; ttl=-30
Age: 3630
Cache-Control: public, max-age=3600, stale-while-revalidate=3600

ttl=-30 means 30 seconds past expiry — being served stale within the stale-while-revalidate window.

Bypassed (client forced revalidation):

HTTP/1.1 200 OK
Cache-Status: MyCDN; fwd=request

Client sent Cache-Control: no-cache — CDN respected it and forwarded.

FAQ

Do all CDNs support Cache-Status?

Not yet — RFC 9211 was published in 2022. Fastly has added support. Others are adopting it. In 2026, you're more likely to encounter vendor-specific headers (CF-Cache-Status, X-Cache) than Cache-Status on most CDNs. Check your CDN's documentation. The standardisation effort is ongoing.

Should I implement Cache-Status on my own caching proxy?

If you're building or operating caching infrastructure, yes — it's the forward-looking standard. Application servers don't need to set it; only actual caches (CDNs, reverse proxies, Varnish, nginx proxy_cache) do.

Fun fact

The X-Cache header proliferation problem is a perfect example of what happens when infrastructure teams all solve the same problem independently without coordination. By 2020, there were at least 15 different X-Cache-style headers across major CDN and caching vendors, all meaning roughly the same thing but formatted differently — making cross-CDN debugging scripts impossible to write generically. RFC 9211 was a direct response to this chaos, driven by engineers who were tired of writing bespoke parsers for every CDN they worked with.