Every response of a name with the cache on carries two headers that say what the cache did, plus Age when it served a stored copy.
curl -sI https://www.example.com/app.css | grep -i -E 'x-cache-status|cache-status|age'X-Cache-Status
Uses the values you may know from other CDNs:
| Value | Meaning |
|---|---|
HIT | Served from the edge cache. |
MISS | Eligible but not cached yet: fetched from the origin and stored when allowed. |
EXPIRED | The stored copy had expired; a fresh one came from the origin. |
REVALIDATED | The stored copy had expired; the origin confirmed it with 304. |
UPDATING | An expired copy served while it refreshes in the background (stale-while-revalidate). |
STALE | An expired copy served because the origin failed. |
BYPASS | Eligible, but the response couldn't be stored (for example Set-Cookie or private). |
DYNAMIC | Not eligible: the cache wasn't looked up (HTML without a rule, a bypass rule, a POST…). |
No header at all means the cache wasn't involved: the answer came from the WAF, a challenge, the access gate or an edge redirect.
Cache-Status
The standard header from RFC 9211, with the reason in detail:
Cache-Status: OpenWAF; hit
Cache-Status: OpenWAF; fwd=uri-miss; stored
Cache-Status: OpenWAF; fwd=stale; fwd-status=304
Cache-Status: OpenWAF; fwd=bypass; detail=set-cookiedetail | With | Meaning |
|---|---|---|
stored | MISS, EXPIRED | The response was stored. |
not-stored, busy | MISS | Storable, but this copy wasn't stored. |
set-cookie | BYPASS | The response sets a cookie. |
private, no-store | BYPASS | The origin's Cache-Control forbids storing it. |
auth | BYPASS | The request had Authorization and the response didn't allow sharing it. |
vary | BYPASS, DYNAMIC | An unsupported Vary, or the request carried a header of a rule's vary_bypass_if_present. |
size | BYPASS | Larger than the maximum object size. |
deception | BYPASS | Blocked by the cache deception armor. |
status | BYPASS | The status code can't be cached. |
uncacheable | BYPASS | No freshness information and no default TTL applies (for example HTML made eligible by a rule without a TTL). |
personal | BYPASS | A personal document requested with cookies or Authorization, without explicit freshness. |
rule | BYPASS, DYNAMIC | A rule's status TTL of -1, or a bypass rule. |
invalid, encoding | BYPASS | An invalid Cache-Control, or a content encoding other than gzip or Brotli. |
extension | DYNAMIC | The extension isn't in the default list and no rule makes the path eligible. |
disabled, dev-mode | DYNAMIC | The cache is off, or development mode is on. |
method, upgrade, body | DYNAMIC | Not a GET/HEAD, a WebSocket upgrade, or a request with a body. |
path, query, key | DYNAMIC | An ambiguous path, a query with caching level No query string, or a key that is too long. |
Age
Sent on HIT, STALE, UPDATING and REVALIDATED: the seconds since the object was stored or last revalidated. An Age sent by your origin on a response that wasn't cached is forwarded unchanged.
Headers the edge removes
OpenWAF-CDN-Cache-Control and Cache-Tag are meant for the edge only and are removed before the response reaches the visitor. An X-Cache-Status sent by your origin is replaced.