Classify the response before selecting a header
A shop can benefit from caching its public logo, product photography and versioned scripts while treating an order page very differently. The key question is whether a response contains data specific to the signed-in customer, such as an address, order history or saved preferences. HTTP has private caches, commonly in browsers, and shared caches, such as proxies and CDNs. RFC 9111 defines what those caches may store and reuse. MDN explains that a cookie alone does not make a response private: a personalised response needs an explicit policy if it must not be stored by a shared cache. Start with an inventory of routes and actual response bodies, rather than assigning one header to the whole website.
For each route, record whether the content is public and identical for everyone, personalised but acceptable to keep in one person's browser, or so sensitive that it should not be intentionally stored in an HTTP cache. Include HTML, JSON endpoints, downloadable documents, redirects and error pages that might echo personal details. A public catalogue page and a signed-in ‘my orders’ page can share styling yet need different cache rules. An account page assembled from public shell content and a personal API response also needs separate treatment for each response. Review both the initial document and background requests, because checking only the visible page header can miss the response that actually carries the data.
Understand what private, no-cache and no-store actually do
The unqualified `private` response directive tells shared caches not to store the response, while allowing a private cache to store it subject to other rules. `no-cache` is often misunderstood: it permits storage but requires validation before reuse. `no-store` tells both private and shared HTTP caches not to store the response. These are distinct choices, not three spellings of the same instruction. For a personalised page that should remain in the user's own browser but be checked with the server before reuse, `private, no-cache` may be a suitable starting policy. For a response containing particularly sensitive session or account material, OWASP's session guidance recommends `no-store`. Classify the material and test the journey before selecting.
Avoid broad promises. RFC 9111 explicitly says `no-store` is not a reliable or sufficient privacy mechanism by itself: malicious or compromised caches may not obey it. It also governs HTTP caching, not every browser storage area or a screenshot someone takes. Conversely, applying `no-store` to every public page can throw away useful browser caching and navigation behaviour. MDN notes costs, including effects on the back/forward cache. Privacy still needs access control, transport security, session handling and careful application code. Cache headers reduce one specific reuse risk; they do not decide who is authorised to request the order in the first place.
Use logout as a focused acceptance scenario
Imagine a customer viewing an address and order history on a shared household computer. They sign out and press Back. The server must reject a fresh protected request without a valid session, but the browser may have a stored page that can still appear during navigation. OWASP warns that sensitive data may remain accessible through browser cache after a session ends and recommends `no-store` on responses containing sensitive session data. Decide which pages need that stronger policy, then test the actual browser behaviour rather than assuming the header produces an identical result in every browser. Explain to users of shared devices that signing out protects future authenticated requests but may not erase every trace already visible on the device.
The worked acceptance route is precise. Sign in as A, open a personal page, sign out, press Back, refresh and reopen the saved URL. Repeat in a second browser and with a second user on the same device. Record what is displayed at each step and whether any network response contains A's data. A stale visual snapshot, a new unauthorised network response and a server response that incorrectly authorises access are different defects requiring different fixes. Also check sensitive downloads, because a PDF may be served by a separate route with different headers. The customer-facing goal is that a person using the shared computer cannot retrieve fresh account data without authentication, and high-sensitivity material is not intentionally retained by HTTP caches.
Keep the policy true as the site changes
Document a small response matrix: public static files, public dynamic pages, personal pages and highly sensitive account responses. For each, state the intended cache audience, freshness rule and test. Review new routes, CDN changes and authentication migrations against that matrix. If a service worker is present, inspect its Cache API rules separately; OWASP notes that HTTP caching headers do not automatically govern entries deliberately stored by that API. Keep tests in both English and Traditional Chinese where routes or page shells differ, even though the header semantics are language-neutral. The best evidence is a captured response and an observed cross-account result from the deployed path, not a configuration comment that claims the policy is active.
This decision is not a contest to maximise the number of `no-store` headers. Public immutable resources should be handled under their own versioning policy; personal responses should be prevented from leaking across users; sensitive responses may need stronger browser-storage restrictions. A clean implementation will sometimes use different directives on two URLs that look visually similar. If a test reveals an old private response already stored, changing a later response to `no-store` does not necessarily remove that old entry; MDN calls out this limitation. Plan remediation and re-test the affected path. The useful result is a documented, inspected distinction between safe reuse and unsafe reuse that staff can maintain when the site grows.
A release check should include failure responses, not only successful pages. An account endpoint might return a personalised error that contains an order number or contact detail, and an edge rule might cache that response under a broad status-code policy. Inspect the response body and headers when the origin is unavailable, the order does not belong to the signed-in user, and the session has expired. Use test data and avoid storing real customer details in test captures. If a cache must serve a public fallback during an outage, ensure the fallback is genuinely public and does not embed a previous user's state. This is also the right moment to verify that a language-specific page varies correctly: two locale versions may be safe to cache publicly, while a personal greeting added to one of them changes the classification. Classify the actual bytes sent, not the route name.
Give the team an explicit exception process for new features. A developer adding a personalised recommendation to an otherwise public catalogue page has changed its cache classification even if the URL and layout are unchanged. The release review should ask which inputs can change the response, whether the cache key represents them, and whether sharing that response is intended. If the answer is uncertain, prevent shared caching until the design is reviewed. Likewise, an account page that fetches personal data after initial load should not be declared safe because the initial HTML contains no name. Inspect the API response and the browser storage path it uses. For an outage, define what a cache may serve when the origin is unavailable; a stale public product description has a different consequence from a stale account balance or order state. Record the chosen rule next to the route owner so later changes can be reviewed against the original reason.
Before you proceed
- Are personalised HTML, API, document and error responses classified separately from public assets?
- Do origin and CDN responses apply the intended cache policy across two test accounts?
- After logout, can a user retrieve fresh private data by Back, refresh or saved URL?


