Dated report · provider-neutral · 23 Sep 2026
Cache deployment and incident runbook
A short, standards-grounded path for keeping private responses private, shipping public content predictably, and finding the layer that served old bytes.
The decision order
- Classify the representation. Is the body identical for every intended audience, or can cookies, authorization, query, language, device, geography, feature flags, time, or server state change it?
- Choose the storage audience. Decide between no HTTP-cache retention, private/browser retention, and shared retention. Do not infer the answer from a provider status field.
- Set browser and shared freshness separately.
max-ageis the browser-facing lifetime;s-maxageis for shared caches and takes precedence there overmax-ageandExpires. - Add validation when retention is acceptable. Use ETag and/or Last-Modified. If both conditions are sent,
If-None-Matchis evaluated first; a 304 does not identify the hop that produced it. - Review identity before TTL. Check method, URI, query,
Vary, cookies, authorization, host, compression, redirects, device/geo signals, and the effective provider key. - Name the hop and its limits. Capture
Age,Cache-Statuswhen exposed, provider fields, validators, a redacted origin marker, and downstream state. A purge or hit at one named layer is not global proof.
Three evidence labels
| Label | Use it for | Do not overclaim |
|---|---|---|
| Standards-stable | HTTP storage audience, freshness precedence, validators, conditional requests, minimum cache-key model, Vary, Age, Via, and optional RFC 9211 Cache-Status. | Standards do not force a cache to store/reuse, identify hidden layers, or control service workers and application caches. |
| Vendor-specific | CloudFront behavior/TTL bounds, response-header policies, invalidation scope, Cloudflare OCC, Cache Rules, Browser TTL, custom keys, status fields, and purge methods. | Do not merge CloudFront and Cloudflare into a generic “CDN,” or turn documentation into a deployment result. |
| Live-topology-required | The matching rule and key, actual stored object, POP/tier, provider status, origin counter, browser state, downstream proxy, or purge follow-up. | No live topology was supplied here. Treat unknown as unknown until a named path is captured. |
Pre-deploy checklist
A · Personalized or sensitive
- List every input that can change the body or sensitive headers: session cookie,
Authorization, tenant, role, query, language, device, geography, experiment, time, and server state. If the list is incomplete, do not make the response public. - Use
Cache-Control: no-storewhen intentional HTTP-cache retention is unacceptable. If browser retention with validation is acceptable, useCache-Control: private, no-cachewith a validator. - Keep the route out of shared caching rules. Review the effective provider rule, edge minimum/override, cache key, Workers or functions, framework caches, service-worker Cache API, and response-header rewrites.
- Do not use
Vary: Cookieas a general privacy fix. A custom key does not make personalized bytes safe; aSet-Cookieheader alone is not a complete cache barrier. - Use a harmless redacted marker and origin counter in an authorized synthetic fixture. Never put credentials, account data, or tokens in the fixture or saved evidence.
- Make a release blocker explicit: a public rule, positive shared-cache minimum, unreviewed key dimension, or one identity receiving another identity's marker stops the rollout.
Containment: correct policy and key first. Purge known provider objects only as a secondary action; it cannot prove that a browser, service worker, corporate proxy, or log has no copy.
B · Public HTML or versioned assets
- Confirm that body and relevant headers are intentionally identical for the audiences that will share them. Put personalization in a private request.
- For mutable HTML, choose explicit, separate lifetimes such as
public, max-age=60, s-maxage=300. This is a policy example, not a provider default or measured result. - Send ETag and/or Last-Modified for mutable content. For a changed fingerprinted asset, publish a new URL such as
/assets/app.20260923.jsand use a long public lifetime only while that URL remains immutable. - Review query,
Vary, cookies, language, device/geo, compression, host, redirects, and the provider's effective key. Keep a query parameter when it changes the bytes. - Record
Cache-Control,Expires, validators,Age,Cache-Statuswhen present, provider fields, and the redacted origin marker from a named vantage. - Retain old assets long enough for old HTML and service workers, unless urgent removal needs an authorized, separately scoped containment action.
Rollback: prefer changing references to a retained version. A purge or invalidation is for a known stable object that must change before its normal lifetime.
Symptom-first incident path
For “old content,” “the browser did not update,” “unexpected sharing,” “the purge did not work,” or “the origin says it was not called,” preserve evidence before clearing caches or purging.
- Record identity. Save UTC time, URL after redirects, method, host, query, relevant request headers, a redacted cookie/auth description, browser/profile, network, navigation type, and displayed body marker or version. Different URLs, query order, redirects, or identities are different tests until proven equal.
- Ask whether a network request occurred. In the affected browser, record Network-panel request/initiator/timing/source details. With no request, check history/back-forward cache and service-worker control/Cache Storage. Chrome's Disable cache changes the experiment and does not bypass service workers, corporate proxies, CDNs, history restoration, or another profile. No request is not proof of an HTTP-cache hit, origin contact, or provider result.
- Repeat from controlled clients. Repeat the same method, URL, redirects, request inputs, and synthetic identity in the affected browser, a fresh profile with worker state recorded, and an authorized HTTP client from the same network. A request directive such as
Cache-Control: no-cacheasks for validation; it is not a universal bypass or purge. - Name the serving hop. Capture status,
Age, optionalCache-Status,Via, provider fields,Cache-Control,Expires, validators,Vary,Set-Cookie,Date, redirects, and request conditionals. A response header may be added, removed, or rewritten at a provider boundary. - Compare a controlled marker. Change only a harmless origin phase from A to B between serialized requests and record a redacted origin counter. A named hit plus an unchanged counter supports reuse at that named cache; an origin increment proves that the instrumented origin received that request. Neither result proves the state of other layers.
- Audit variants and configuration. Compare query/order, cookies, authorization, language, encoding, origin, device/geo, host/scheme, redirects, method, and worker control. Read the effective matching rule and cache key. For unexpected sharing, stop the public rule if authorized, preserve captures, and assess exposure before purge.
- Close with a scope statement. Include URL, request variant, client/profile, named hop, time, and evidence. Say “this named cache reused this response for this request,” not “the CDN was stale,” unless the complete topology was measured.
What an observation proves
| Observation | Scoped reading | Does not prove |
|---|---|---|
pageshow.persisted === true and no request | History/back-forward cache can explain the display. | That the HTTP cache supplied it. |
Worker log reports a Cache.match hit | The worker/Cache API supplied this client response. | That browser HTTP or edge storage is empty. |
Named Cache-Status: edge-test; hit and unchanged origin counter | The named cache reports satisfying this request without forwarding. | Other POPs, hidden hops, or other users have the same result. |
| Origin counter increments and marker B is emitted | The instrumented origin received this request and produced B at that time. | That a browser, worker, or upstream cache cannot later display A. |
304 Not Modified with a matching ETag | The recipient evaluated a conditional request and said the stored representation could be reused. | That the recipient was the origin, or that a purge occurred. |
No Age, Cache-Status, or provider field | No positive diagnostic was exposed at this vantage. | Origin contact, cache absence, or privacy safety. |
Versioned URL versus named-provider purge
| Need | Prefer | Boundary |
|---|---|---|
| Routine JS/CSS/image/font release | New versioned or content-hash URL, then update HTML and manifests. | The old URL, old HTML, service worker, bookmark, or proxy can still exist. |
| Mutable HTML at a stable URL | Short explicit freshness plus validators; narrow named-provider invalidation only when urgent. | Browser history/cache, service worker, corporate proxy, and other hops may still show old HTML. |
| Emergency correction of a known provider object | Authorized exact URL/path/tag/prefix purge or invalidation, followed by a same-path fetch. | API success or completion proves the named operation, not universal eviction. |
| Rollback | Restore references to a retained prior version; invalidate stable HTML only if needed. | Documents that still reference another URL need their own update or expiry. |
| Private-response exposure | Correct representation policy and key; contain each named layer; purge known provider copies second. | No purge proves that browser, worker, proxy, log, or another hop has no copy. |
Browser/service-worker/corporate-proxy limits: a new URL makes a new target; it does not update a client that never receives new HTML or worker code. A named-provider purge changes only the provider's documented scope. Keep those boundaries in the incident record.
Named vendor sidebars
CloudFront · vendor-specific
- Read the selected behavior and effective
MinTTL,DefaultTTL,MaxTTL, cache policy, key, and response-headers policy together. - A positive CloudFront minimum can make the edge cache despite origin
no-cache,no-store, orprivate. This is a CloudFront setting, not a new HTTP rule. - A response-headers policy can add or rewrite viewer
Cache-Controlwithout changing CloudFront object caching. Viewer headers are not edge-TTL proof. - Path invalidation is scoped to the named distribution and effective path/key rules. Versioned names help around local or corporate copies; neither is global deletion.
- Configured CloudFront
Server-Timingcache metrics are optional product evidence. Absence is unknown.
Cloudflare · vendor-specific
- Read the zone's Origin Cache Control state and matching Cache Rule. Edge Cache TTL can respect, bypass by default, or override origin signals; Browser TTL is separate.
- Cloudflare documentation gives product-specific meanings to
CF-Cache-StatusandAge. Record the named path, tier/POP context, and configuration; do not use a viewer header as hidden Edge TTL proof. - Default and custom cache keys can select URL/query/header/cookie/host/device/geo/language dimensions subject to product and plan. A custom key is not a privacy guarantee.
- URL, tag, prefix, hostname, and everything purges have different blast radii. A purge API 200 acknowledges receipt; it does not prove the object existed or was evicted everywhere.
- Current Cloudflare pages describe
no-cachedifferently in generic and OCC-specific contexts. Resolve the actual zone state with an authorized capture; do not generalize either wording.
What this proves / does not prove
What this report proves from standards and current documentation
- HTTP separates storage audience, freshness, validation, key matching, stale use, named evidence, and browser/history/application boundaries.
no-cacheis notno-store;privateis not encryption;s-maxageis a shared-cache lifetime;If-None-Matchwins overIf-Modified-Sincewhen both are sent.- CloudFront and Cloudflare document independent controls for edge eligibility/lifetime, viewer/browser lifetime, cache key, and invalidation/purge. Those controls remain named.
- The common checks are representation review, storage audience, explicit browser/shared freshness, validators, key review, named-hop evidence, and downstream testing.
What this report does not prove
- No CloudFront or Cloudflare response was observed as HIT, MISS, BYPASS, REVALIDATED, EXPIRED, stale, or privacy-leaking here.
- No origin header alone proves the selected provider's edge behavior, and no 200, 304, old body, missing header, browser label, or purge receipt identifies every producer.
- A fresh profile, direct client, one POP, one provider status, or one A/B marker does not prove that all alternate keys, tiers, partitions, browsers, workers, corporate proxies, or other hops are clear.
- Versioned URLs do not delete old URLs. Provider invalidation/purge does not clear browsers, service workers, corporate proxies, history, other CDNs, or application caches.
Uncertainty: an authorized live probe still needs a fixed URL, sanitized controlled origin marker/counter, selected provider configuration, request variants, named vantage, and permission to capture and, if needed, purge synthetic objects. The current synthesis does not claim that probe was run.
Sources and continuity
Direct source pages were retrieved 23 Sep 2026 during 06:31:30–06:35:34 UTC. Standards are normative; browser pages are browser-specific; CloudFront and Cloudflare pages describe named products. The public evidence ledger records the same source set and retrieval date.
| Class | Direct source | Used for |
|---|---|---|
| Standards-stable | RFC 9111 · HTTP Caching | Storage, key selection, freshness, validation, stale use, Age, directives, and browser/application boundary. |
| Standards-stable | RFC 9110 · HTTP Semantics | Validators, Vary, conditional precedence, 304, and Via. |
| Standards-stable | RFC 5861 · stale controls · RFC 9211 · Cache-Status | Bounded stale extensions and optional named-cache diagnostics. |
| Browser | MDN · HTTP caching · MDN · Cache-Control · MDN · conditional requests | Developer-facing explanations of private/shared caching, directives, versioned assets, and validators. |
| Browser | Chrome Network panel · Chrome Application panel · Chrome bfcache guidance | Request logging, Disable cache limits, service workers, Cache Storage, and history restoration boundaries. |
| CloudFront | Expiration · Cache policies · Cache key | TTL bounds, origin interaction, and key dimensions. |
| CloudFront | Response headers policies · Invalidation · Versioned objects | Viewer-header boundary, named invalidation, and versioned URL limits. |
| Cloudflare | Cache Rules · Cache Rule settings · Origin Cache Control | Rule precedence, Edge/Browser TTL, OCC modes, and product-specific directive handling. |
| Cloudflare | Cache responses · Revalidation · Cache keys | CF-Cache-Status, Age, revalidation, and default/custom key dimensions. |
| Cloudflare | Purge overview · Single-file purge · Cache-key purge | Purge receipt, scope, custom-key inputs, and downstream limits. |
Earlier public reports
- HTTP caching basics — freshness, storage, private/shared, and validation baseline.
- ETag versus Last-Modified — conditional precedence.
- Cache-key and privacy mismatch — request variants and storage safety.
- Locating the serving layer — browser, worker, intermediary, and origin evidence.
- CloudFront TTL policy and invalidation — named provider audit.
- Cloudflare Cache Rules and Origin Cache Control — named provider case.