no-cache vs no-store: When to Use Each
TL;DR — no-cache permits caching but mandates revalidation with the origin before every reuse; no-store forbids storage entirely. Confusing the two either leaks sensitive data through cached copies or floods the origin with redundant full transfers.
Quick-reference header block for the two canonical scenarios:
# Validate-before-serve (freshness replaced by conditional check)
Cache-Control: no-cache, private
# Absolute storage prohibition (regulatory / sensitive data)
Cache-Control: no-store, private
Mechanism and RFC 9111 Alignment
no-cache — conditional revalidation required
RFC 9111 Section 5.2.2.4 defines no-cache as follows: a cache must not reuse a stored response to satisfy a new request without first forwarding that request to the origin for validation. This is not a prohibition on storage — it is a prohibition on serving without checking.
The validation exchange uses the stored validators:
ETag/If-None-Match— strong or weak entity tag comparisonLast-Modified/If-Modified-Since— timestamp comparison
If the origin returns 304 Not Modified, the cache reuses the stored response body without downloading it again. The round-trip costs a few milliseconds but avoids retransmitting the payload. Only when the origin returns 200 OK (with an updated ETag) does the full payload transfer.
This behavior is functionally equivalent to max-age=0, must-revalidate, but no-cache states the intent more clearly and is recommended in RFC 9111 for this purpose.
no-store — storage prohibited at every layer
RFC 9111 Section 5.2.2.5: caches must not store any part of a response — including headers — when no-store is present. This applies to browser memory cache, browser disk cache, CDN edge nodes, reverse proxies, and enterprise gateway caches.
Every request fetches the full payload from origin. No 304 is possible because there is no stored copy to validate against.
Key difference table:
| Property | no-cache |
no-store |
|---|---|---|
| May be stored in cache? | Yes | No |
| Reuse without origin check? | No | No (nothing stored) |
Can save bandwidth via 304? |
Yes | No |
| Applies to browsers? | Yes | Yes |
| Applies to CDNs / proxies? | Yes | Yes |
| RFC 9111 section | 5.2.2.4 | 5.2.2.5 |
Scope and Precedence
no-store holds the highest precedence in the directive resolution order described in Header Stacking and Directive Precedence. It immediately overrides max-age, s-maxage, public, and any freshness extension (stale-while-revalidate, stale-if-error, immutable). Emitting max-age=3600, no-store is valid syntax but the TTL directive is inert — no storage occurs.
no-cache sits in the second precedence tier. It permits storage but forces validation; must-revalidate adds the guarantee that stale entries must not be served even under error conditions (network failure, origin timeout). Without must-revalidate, some caches may serve stale content under RFC 9111’s error-tolerance provisions (Section 4.2.4).
Precedence table (highest to lowest):
| Priority | Directive | Effect |
|---|---|---|
| 1 | no-store |
Absolute storage prohibition |
| 2 | no-cache |
Storage allowed; revalidation required |
| 3 | private |
Browser-only scope |
| 4 | public |
Shared-cache authorized |
| 5 | s-maxage |
Shared-cache TTL |
| 6 | max-age |
All-cache TTL |
| 7 | must-revalidate |
No stale-on-error after TTL |
no-cache applies symmetrically to both private caches (browsers) and shared caches (CDNs). CDNs must not serve a cached no-cache response without forwarding a conditional request to origin. For CDN-specific cache scope interactions, see Public vs Private Cache Scope.
Implementation Patterns
Pattern 1 — Frequently changing API data
An inventory count, exchange rate, or live score is safe to cache but must be fresh on every serve. Bandwidth savings via 304 are significant at scale.
Cache-Control: no-cache, private
ETag: "inv-a3f9"
Every client revalidation sends If-None-Match: "inv-a3f9". If the count has not changed, origin returns 304 — the client reuses the stored body, no payload crosses the wire.
Pattern 2 — Authenticated HTML pages
HTML that varies per user must not be served from a shared cache but may still benefit from browser-level 304 savings:
Cache-Control: no-cache, private
Vary: Cookie
The Vary: Cookie directive prevents CDNs from sharing responses across sessions. no-cache ensures the browser validates on every navigation without transmitting the full HTML body unnecessarily.
Pattern 3 — Session tokens and CSRF state
Tokens must never be retained anywhere. no-store is the correct choice:
Cache-Control: no-store, private
Pragma: no-cache
Including Pragma: no-cache maintains backward compatibility with HTTP/1.0 intermediaries that do not parse Cache-Control.
Pattern 4 — PII, financial records, medical data
Regulatory requirements (HIPAA, PCI DSS) require zero persistence of sensitive payloads:
Cache-Control: no-store, private
X-Content-Type-Options: nosniff
no-store satisfies the “must not retain” requirement. Neither CDN edge nodes nor browser disk cache will hold any part of the response.
Pattern 5 — Authentication pages (/login, /logout)
Form state must not persist between sessions, even in the browser’s back/forward cache:
Cache-Control: no-store, private
Clear-Site-Data: "cache"
Clear-Site-Data: "cache" instructs the browser to evict cached entries for this origin on logout — a belt-and-suspenders approach when no-store alone is not sufficient for post-logout cleanup.
Server and CDN Configuration
Nginx
# Dynamic API — cache but always validate
location /api/ {
add_header Cache-Control "no-cache, private" always;
}
# Session-bound data — no storage at any layer
location /auth/ {
add_header Cache-Control "no-store, private" always;
}
# Login / logout pages
location ~ ^/(login|logout) {
add_header Cache-Control "no-store, private" always;
add_header Clear-Site-Data '"cache"' always;
}
The always flag ensures the header is emitted on error responses (4xx, 5xx) — without it, Nginx suppresses custom headers on non-2xx responses, which can allow error pages to be cached by intermediaries.
Apache
# Dynamic API — revalidate on every request
Header always set Cache-Control "no-cache, private"
# Sensitive endpoints — no storage
Header always set Cache-Control "no-store, private"
Cloudflare (Page Rules / Cache Rules)
Cloudflare maps no-store to a hard BYPASS on its edge. To enforce this via a Cache Rule in the Cloudflare dashboard:
- Create a Cache Rule matching the URL pattern (e.g.,
auth.example.com/session/*). - Set Cache Status to Bypass.
- Cloudflare will forward all requests to origin and return
CF-Cache-Status: BYPASSin responses.
For no-cache routes, Cloudflare should be configured to respect origin headers rather than override them. Disable any “Edge Cache TTL” override on these paths so Cloudflare honors the origin no-cache instruction and forwards conditional requests.
Interaction with Related Directives
no-cache + must-revalidate
must-revalidate strengthens no-cache by removing the error-tolerance exception. Without it:
Cache-Control: no-cache
…a cache may serve a stale entry if the origin is unreachable (RFC 9111 Section 4.2.4). Adding must-revalidate:
Cache-Control: no-cache, must-revalidate
…forces the cache to return a 504 Gateway Timeout rather than serve stale content. Use no-cache, must-revalidate for financial data, configuration endpoints, or anything where serving stale is worse than serving an error.
no-store + ETag
Combining these is contradictory. no-store prohibits any storage, which means no ETag can ever be stored or compared. The ETag header is ignored when no-store is present. See freshness vs validation models for guidance on which responses benefit from validators.
no-cache + s-maxage
This combination is valid but niche — it instructs shared caches to store the response but still revalidate on every request, rather than serving from the stored copy during the s-maxage window. The practical result is the same as no-cache alone for CDNs that respect the directive. However, some CDNs use s-maxage to populate their cache index even when no-cache is also present, enabling PURGE API calls to invalidate by URL. Check your CDN’s documentation.
For the full set of directive combinations and how stacking resolves conflicts, see the mastering max-age and s-maxage reference.
Verification Workflow
Step 1 — Inspect response headers
curl -sI https://api.example.com/resource
For no-cache: expect Cache-Control: no-cache and an ETag or Last-Modified header in the response. The absence of validators means revalidation will always result in a 200, defeating the bandwidth-saving purpose of no-cache.
For no-store: expect Cache-Control: no-store. There must be no Age header (an Age header indicates the response came from a cache). At a CDN layer, expect CF-Cache-Status: BYPASS (Cloudflare) or X-Cache: MISS, uncacheable (Fastly).
Step 2 — Verify no-cache revalidation via conditional request
# Extract ETag from first response
ETAG=$(curl -sI https://api.example.com/resource | grep -i etag | awk '{print $2}' | tr -d '\r')
# Send conditional request
curl -sI -H "If-None-Match: ${ETAG}" https://api.example.com/resource
A correctly configured no-cache endpoint returns 304 Not Modified when the resource has not changed. A 200 response is correct when the resource has changed. A 200 response when the resource has not changed indicates the origin is ignoring If-None-Match — fix the origin’s ETag comparison logic.
Step 3 — Confirm no CDN bypass for no-cache
curl -sI https://api.example.com/resource | grep -i "cf-cache-status\|x-cache"
For no-cache, CDN status should be REVALIDATED or MISS (not HIT). A HIT response indicates the CDN is ignoring the no-cache directive and serving a stale copy — check CDN cache rules for TTL overrides.
For no-store, CDN status must be BYPASS or DYNAMIC. Any other status indicates the CDN is storing the response.
Step 4 — Browser DevTools verification
- Open DevTools → Network tab.
- With caching enabled, load the page once to populate any stores.
- Reload without disabling cache.
- For
no-cacheresources: expect304 Not Modifiedwith a size of(from cache)or similar — the body came from the stored copy, but the request reached the origin. - For
no-storeresources: expect200 OKon every load with the full payload size and nofrom cacheannotation.
A 200 from memory cache on a no-cache resource indicates a browser bug or a browser extension intercepting requests.
Failure Modes and Gotchas
-
WebKit
no-cachetreated asno-store— Older Safari and WebKit-based browsers have historically treatedno-cacheas equivalent tono-storefor certain cross-origin responses. For sensitive data where any storage risk is unacceptable, useno-storeexplicitly rather than relying onno-cachesemantics. -
CDN ignoring
no-cachedue to positive TTL rule — Many CDN configurations include a blanket “cache everything for X hours” page rule. This overrides originno-cacheinstructions. Always verify CDN cache rules do not have positive TTL overrides on paths that emitno-cache. -
no-storeon responses withVary— IncludingVaryon ano-storeresponse is harmless but meaningless.Varyaffects how caches partition stored entries; if nothing is stored, theVaryheader is ignored. Removing it reduces response header bloat. -
Missing validators on
no-cacheresponses —no-cacherequires the origin to respond to conditional requests. If the response carries noETagand noLast-Modified, every revalidation will result in a200with the full payload — identical to not caching at all. Emit at least one validator on everyno-cacheresponse. -
no-cachein request headers vs response headers — RFC 9111 definesno-cachein both request context (the client demands a fresh response, bypassing any cache) and response context (the server requires revalidation). These are distinct semantics. This page covers response-sideno-cache. Browser DevTools “Disable cache” checkbox sets request-sideno-cacheon every request. -
Pragma: no-cacheinconsistency — HTTP/1.1 deprecatedPragma. It is defined only in request context in RFC 9111 and has no normative response semantics. Some implementations honour it in responses; do not rely on it. SetCache-Controlexplicitly. -
no-storeand service workers — Service workers maintain their own cache (CacheAPI) that is separate from the HTTP cache.no-storedoes not prevent a service worker from caching a response. If a service worker intercepts a request and stores the response in its own cache,no-storeon the HTTP layer does not prevent that. Sensitive data must be explicitly excluded from service worker caching logic.
FAQ
Q: Does no-cache mean “do not cache”?
No. The name is misleading. no-cache in a response means “you may cache this, but you must validate it with the origin before every use.” The correct directive to prevent caching is no-store.
Q: Should I combine no-cache and no-store in the same header?
Rarely. If you need absolute storage prohibition, use no-store alone — it supersedes no-cache. The only practical reason to combine them is for HTTP/1.0 proxy compatibility: HTTP/1.0 caches do not understand no-store, so adding no-cache alongside covers older intermediaries. In modern infrastructure, no-store alone is sufficient.
Q: Is no-cache equivalent to max-age=0?
Functionally similar but not identical. max-age=0 makes the response immediately stale, which triggers revalidation on the next request — but only if the cache decides to revalidate (which depends on implementation). no-cache makes revalidation mandatory; the cache must not serve without checking even if it considers the entry “fresh.” In practice, treat them as equivalent for browsers, but use no-cache when the intent is “always validate.”
Q: Why does my CDN still cache responses with no-cache?
CDN caching is governed by CDN configuration as well as origin headers. A page rule or cache rule with a positive TTL override will win over origin Cache-Control: no-cache. Review your CDN’s cache rule priority and ensure no rule is applying a TTL to the affected paths.
Q: Can I use no-cache for static assets?
Not typically. Static assets with content-hashed filenames (e.g., app.a3f9d2.js) should use Cache-Control: public, max-age=31536000, immutable — the content hash guarantees freshness; revalidation adds unnecessary latency. Use no-cache only for mutable URLs where the content may change between visits.
Related
- Implementing
no-transformfor Compressed Assets — howno-transformprevents CDN compression of responses that must not be modified, often paired withno-storeon sensitive payloads. - Header Stacking and Directive Precedence — the full resolution order when multiple
Cache-Controldirectives arrive in a single header string. - Freshness vs Validation Models Explained — how
ETagandLast-Modifiedvalidators enable the304savings that makeno-cacheworthwhile. Cache-ControlBest Practices for REST APIs — production patterns for API responses, including when to useno-cacheon mutable collection endpoints andno-storeon user-specific data.