no-cache vs no-store, which Cache-Control directive keeps data private
CentralCSP Team ·
Last update:
The names sound like synonyms, and that is the trap. no-cache does not mean "do
not cache." It means "store it, but check with the server before reusing it." The
directive that actually keeps a response out of caches is no-store, and for a
page with personal data that difference is the whole ballgame.
If you have a response carrying a session ID or personal data, the
Cache-Control directive
you want is no-store. no-cache still lets the browser and shared caches keep
the bytes, it only forces a revalidation before reuse. For confidentiality,
stored-but-revalidated is not good enough; you want never-stored.
no-cache vs no-store, what each one tells a cache
Both directives come from RFC 9111, the HTTP caching specification, and it defines them precisely. The confusion is entirely in the names, so start with what each one actually instructs a cache to do.
There are two tiers of cache to keep in mind. A private cache is the user's browser.
A shared cache is a corporate proxy, a CDN, or a reverse proxy that stores one
response and reuses it for many users. HTTP is designed to cache aggressively, so a
response with no Cache-Control header at all can still be stored and reused
through heuristic caching (RFC 9111 section 4.2.2, with a heuristic lifetime
typically around 10% of the resource's Last-Modified age). And the spec is
explicit that Set-Cookie does not inhibit caching: a cacheable response with a
Set-Cookie header can be, and often is, used to satisfy other requests. If you
want a response kept out of caches, you have to say so.
no-cache stores the response, then revalidates
no-cache (RFC 9111 section 5.2.2.4) means a cache may store the response but
must not reuse it without a successful revalidation with the origin server.
Storage is permitted, not mandated, so the copy can sit on disk or in memory. On
the next request the cache asks the server "is my copy still good?" and reuses it
only on a 304. MDN states it verbatim: "no-cache does not mean don't cache."
Cache-Control: no-cacheFor confidentiality this does nothing. The response can be sitting in the cache,
and anyone with access to it, the shared proxy or the user's disk on a shared
machine, can read the stored copy. Revalidation controls freshness, not who can see
the bytes. It also does not bind your edge. Cloudflare respects origin
Cache-Control by default, but cache rules and edge TTL settings can augment or
override it, so no-cache at the origin does not constrain a misconfigured CDN
layer.
no-store keeps the response out of every cache
no-store (RFC 9111 section 5.2.2.5) is the strict one. A cache "MUST NOT store
any part of either the immediate request or the response and MUST NOT use the
response to satisfy any other request." That applies to private and shared caches
alike.
Cache-Control: no-storeThis is the directive for any response containing a session identifier or personal
data, and it is what OWASP's Session Management cheat sheet prescribes for
responses carrying session IDs. With no-store, the extra directives and the
Expires header people pile on are generally unnecessary for modern browsers. The
legacy incantation private, no-cache, no-store, max-age=0, must-revalidate is,
per MDN's own example, equivalent to plain no-store, because the most restrictive
directive wins.
One honest caveat from the spec itself: RFC 9111 warns that no-store "is not a
reliable or sufficient mechanism for ensuring privacy," because malicious or
non-compliant caches might not obey it. It is an instruction to well-behaved
caches, not encryption. Use it, and do not treat it as the only control.
private only stops shared caches
private (RFC 9111 section 5.2.2.7) says shared caches must not store the response,
but the browser's own cache still may. It scopes a personalized response to one
user by keeping it out of proxies and CDNs, which is useful, but it is not a
confidentiality control against local storage of the bytes. private on its own
still leaves the response on the user's disk.
Cache-Control: private, no-cacheThat combination, private, no-cache, is a reasonable middle ground for a
personalized page that is fine to cache in the browser but must revalidate and must
never touch a shared cache. It is not the setting for a session token; that is
still no-store.
Getting this tier wrong has a track record. Web cache deception attacks work
precisely when dynamic per-user content is storable by a shared cache, and the
defense is no-store or private on dynamic responses. In the Steam incident of
Christmas 2015, a caching layer served authenticated store pages to the wrong
users, and pages belonging to about 34,000 users, some including billing
addresses and partial card digits, were shown to other customers.
no-cache vs no-store vs private, side by side
| Directive | May be stored? | Reused without asking the server? | Which caches |
|---|---|---|---|
no-store | No | Never stored | Private and shared |
no-cache | Allowed (disk or memory) | No, revalidates first | Private and shared |
private | Browser only | Yes, while fresh | Private only, shared must not store |
The back/forward cache plays by different rules
The back/forward cache (bfcache) is not an HTTP cache. It keeps a live snapshot of
a page so navigating back is instant, and because it restores the snapshot without
revalidating, Cache-Control only partially governs it. MDN notes that even
no-cache does not guarantee revalidation for history navigations.
Chrome now restores even no-store pages from bfcache (the rollout to all users
completed in 2025), with mitigations: the page is not restored if it used
WebSocket, WebTransport, or WebRTC, the entry is evicted when cookies or other
authorization methods change or when a subresource fetch comes back with
no-store, and the entry is capped at 3 minutes instead of the usual 10. Chrome's documentation
notes that other browsers may still block bfcache when Cache-Control: no-store
is present, so do not assume uniform behavior across engines.
The security consequence is concrete: a user who logs out and hits the back button
can be shown the authenticated page again. So logout has to actually invalidate the
session, changing or clearing the session cookie
server-side, not just navigate away. Because bfcache eviction is tied to a cookie
change, clearing the session cookie is what makes the restored page safe, not the
cache directive. OWASP additionally recommends clearing client state on logout with
a Clear-Site-Data header.
Clear-Site-Data: "cache", "cookies", "storage"The directives are quoted strings and the header only works over HTTPS. Support for
"cookies" and "storage" is broad; support for "cache" is uneven across
engines, so treat it as an extra layer rather than the mechanism. For browsers
where a bfcache restore is still possible, web.dev suggests a belt-and-suspenders
check: listen for the pageshow event, verify a site-specific cookie is still
present, and force a reload if it is gone.
The mistake also runs in reverse. Do not use no-store as a bfcache blocker on
ordinary pages. web.dev's guidance is to reserve no-store for pages where caching
of any sort is never appropriate, and to use no-cache or max-age=0 elsewhere,
since those do not affect a page's bfcache eligibility. Blanket no-store kills
instant back/forward navigation for no security gain.
Recommendation
Set caching per response, not site-wide. Send Cache-Control: no-store on any
response that carries a session ID or sensitive data, login, account, checkout,
anything personalized. Keep long-lived caching where it belongs, on static
fingerprinted assets, with max-age=31536000, immutable.
Cache-Control: max-age=31536000, immutableOne note on immutable: Firefox and Safari honor it, Chrome and Edge ignore it.
Ignoring it is harmless, the max-age still applies, so the pattern stays correct
everywhere.
And pair no-store with a real logout: invalidate the session server-side, clear
the session cookie, and send Clear-Site-Data so a restored bfcache page cannot
show authenticated content. To find responses that set or require a session cookie
but carry no directive keeping them out of shared caches, CentralCSP's free
security headers scanner flags them so you can add
no-store before it becomes a leak.
FAQ
Why is Cache-Control no-cache not working?
It usually is working, exactly as specified. no-cache permits storage and permits
reuse after a 304 revalidation, so cached copies on disk and fast repeat loads are
expected. Back-button restores can bypass it entirely, because the bfcache restores
a snapshot without revalidating. And a CDN layer with its own cache rules may
override origin headers. If the behavior you wanted was "never stored," the
directive you wanted was no-store.
Does no-cache prevent caching?
No. It prevents reuse without revalidation, not storage. MDN says it directly: if
the sense of "don't cache" you want is actually "don't store," then no-store is
the directive to use.
Which Cache-Control directive should I use for sensitive data?
Cache-Control: no-store on every response carrying a session ID or personal data.
That is OWASP's recommendation, and the extra directives and Expires header are
generally unnecessary for modern browsers.
Can the back button show a page that was sent with no-store?
Yes, in Chrome. no-store keeps the response out of HTTP caches, but Chrome
restores even no-store pages from the bfcache, with eviction on cookie changes
and a 3 minute cap. The back button can still show the page unless logout changed
or cleared the session cookie. Pair server-side session invalidation with
Clear-Site-Data, and optionally a pageshow cookie check that reloads the page.
Should I use no-store on every page?
No. Reserve no-store for responses where caching of any sort is never
appropriate, anything carrying a session ID or personal data. On ordinary pages it
forces a full re-download on every request and can block back/forward cache
restores in some engines, so navigation gets slower with no security gain. Use
no-cache or max-age=0 instead.
Related reading
- How to improve your security headers grade
- SameSite vs CSRF tokens, which protects your sessions
- Cache-Control reference
- Cookie security reference
Sources
- RFC 9111, HTTP Caching
- MDN, Cache-Control
- MDN, HTTP caching guide
- Chrome, bfcache and Cache-Control no-store
- web.dev, Back/forward cache
- OWASP, Session Management cheat sheet
- OWASP WSTG, Testing for Browser Cache Weaknesses
- MDN, Clear-Site-Data
- PortSwigger, Web cache deception
- Cloudflare, Cache-Control at the edge