Try the HTTP Headers

Cache-Control Demystified: Why "no-cache" Doesn't Mean "Don't Cache," and Other Misread Directives

"Cache-Control: no-cache" doesn't mean "don't cache this" — it means "cache it, but check with the server before using the cached copy, every time." This single misconception is responsible for much "why is this still cached" confusion. Here's the real no-cache vs no-store distinction, why private vs public matters for shared CDN caches, what must-revalidate does when the server is unreachable, and why Vary headers matter for content that differs by language or compression.

June 19, 2026 7 min read
Share: Facebook WhatsApp LinkedIn Email
Cache-Control Demystified: Why "no-cache" Doesn't Mean "Don't Cache," and Other Misread Directives

A header set to "no-cache" doesn't mean "don't cache this" — it means "cache this, but check with the server before using the cached copy every time" — and this single, widely-misunderstood directive is responsible for a large fraction of "why is this still cached" confusion

The previous articles on this site covered HTTP caching (Cache-Control, ETags), security headers, and CORS. This article addresses specific Cache-Control directive combinations and misconceptions — particularly the no-cache vs no-store distinction, which is frequently confused, despite being one of the most consequential differences in how browsers/caches behave.


no-cache: caches are allowed to store the response — they just must revalidate first

Despite its name, Cache-Control: no-cache permits storing the response in a cache — what it prohibits is using the cached copy without first checking with the origin server (a process called revalidation, typically using ETags or Last-Modified headers, covered in the previous caching article).

The practical effect: a resource with no-cache is stored, but *every subsequent request must include a conditional request (using the cached ETag/Last-Modified value) to the origin server — the server responds with either 304 Not Modified (if the resource hasn't changed — in which case the cached body is used, avoiding re-downloading the full content) or a fresh 200 OK with new content (if it has changed).

no-cache is, essentially, "always revalidate"not "never cache" — it still provides bandwidth savings (the body isn't re-downloaded if unchanged, thanks to 304 responses) — but requires a round-trip to the server for every request, unlike max-age-based caching (where, within the max-age window, the cached copy is used directly, with no server round-trip at all).


no-store: the actual "don't cache this" directive

Cache-Control: no-store means: the response must NOT be stored in any cache, at allnot in browser caches, not in intermediate/proxy caches — **every request must go fully to the origin server, and the response is used, displayed, and then discardednot retained for any future use.

no-store is the directive for genuinely sensitive/dynamic contentbanking information, personalized data that shouldn't persist in any cache (even temporarily, even for revalidation purposes) — responses that must never be reused, under any circumstances, even if "unchanged."

The frequent confusion: developers intending "don't cache this (sensitive) content" sometimes use no-cachewhich, as described, still stores the responsewhile it does require revalidation (providing some protection — the server is consulted on every request), it's not the "absolutely never stored anywhere" guarantee that no-store provides. For genuinely sensitive content, no-store (often combined with no-cache and private, for maximum clarity/compatibilityCache-Control: no-store, no-cache, must-revalidate, private is a commonly-seen "maximum strictness" combination, though no-store alone is, technically, the strongest individual directive) is the more appropriate choice.


private vs public: who's allowed to cache?

Cache-Control: private indicates the response is specific to one user (e.g., a personalized page, reflecting the requesting user's account/session) — private responses may be cached by the user's own browser, but must NOT be cached by shared/intermediate caches (CDNs, corporate proxy caches, ISP-level caches) — since a shared cache serving this response to a different user would be serving that different user content intended for the original user — a significant privacy/correctness issue.

Cache-Control: public indicates the response is the same for everyonesuitable for caching by shared/intermediate caches as well as the requesting browser.

The consequence of omitting private for personalized content cached by a shared CDN/proxy: user A's personalized page might be cached by a shared cache (which doesn't know "this is personalized, don't share it" without the private directive) — and subsequently served to user B, when user B requests what appears, to the cache, to be "the same" URLresulting in user B seeing user A's personalized content — a genuine, documented class of privacy/security incident, underscoring why "private" vs "public" isn't a minor technicality.


must-revalidate: what happens when a stale cached response can't reach the server

max-age=N specifies how long a cached response is considered "fresh" — after N seconds, the response is "stale."

Without must-revalidate: some caches, upon encountering a stale response, and being unable to reach the origin server (network issues, server downtime) — may serve the stale response anyway ("better than nothing" — some HTTP caching specifications permit, under certain conditions, serving "stale" content when revalidation isn't possible, as a resilience measure).

must-revalidate instructs caches: do NOT serve a stale response under any circumstancesif the cached copy is stale, and the origin server can't be reached to revalidate — the cache must return an error (rather than the stale, potentially-outdated content) — appropriate for content where serving outdated information is worse than serving an error (e.g., real-time pricing/availability information, where "stale" data could lead to incorrect decisions based on it).


Vary: caching the "same" URL differently based on request headers

The Vary response header indicates that the cached response depends on one or more request headers — e.g., Vary: Accept-Language indicates the response content differs based on the requesting client's Accept-Language header (the server returns different-language content for the same URL, depending on what language the client requested).

Why this matters for caching: without Vary, a cache might store one version of the response (e.g., the English version, served to the first requester) and serve that same cached version to subsequent requesters, regardless of their own Accept-Language headera French-speaking user, requesting the same URL after an English-speaking user's request was cached, would receive the cached English versionincorrect, from the French-speaking user's perspective.

Vary: Accept-Language tells caches: "store separate cached copies, keyed by Accept-Language value, for this URL" — ensuring each language-variant is cached and served correctly, independently.

A commonly-overlooked Vary use case: Vary: Accept-Encodingresponses might be served compressed (gzip, brotli) or uncompressed, depending on the client's Accept-Encoding headerwithout Vary: Accept-Encoding, a cache could serve a compressed response to a client that doesn't support (or didn't request) that compressionresulting in the client receiving garbled, undecoded compressed data (since the client, not expecting compressed content, wouldn't decompress it).


How to use the HTTP Headers tool on sadiqbd.com

  1. Check Cache-Control directive combinations on responses — particularly distinguishing no-cache (stored, but revalidated every time) from no-store (never stored at all) — for sensitive content, verify no-store is present, not just no-cache
  2. For personalized content: verify private is present (not public, and not absentabsence of either may, depending on other directives/defaults, be ambiguous) — particularly important for content served through CDNs/shared caches
  3. Check for Vary headers on content that varies by language/encoding/other request headers — absence of an appropriate Vary header, for content that genuinely varies, can indicate a caching-correctness issue (even if it hasn't yet manifested as a visible bugit may simply not have been triggered yet, depending on request patterns)

Frequently Asked Questions

If no-cache still allows storage, what's the practical difference from max-age=0? max-age=0 also means the response is immediately "stale" (0 seconds of freshness) — requiring revalidation before reusein practice, Cache-Control: no-cache and Cache-Control: max-age=0 often produce similar behavior (both require revalidation on every request) — though historically, some differences in how various caches/browsers interpret these directives (particularly combined with other directives, or in edge cases involving older HTTP/1.0-era caches, which no-cache was, in part, designed with in mind) have existed — for modern purposes, the two are generally treated similarly by current browsers, but no-cache remains the more commonly-used, more widely-recognized directive for expressing "always revalidate" intent.

Is the HTTP Headers tool free? Yes — completely free, no sign-up required.

Try the HTTP Headers tool free at sadiqbd.com — inspect Cache-Control, Vary, and all response headers for any URL instantly.

Share: Facebook WhatsApp LinkedIn Email

HTTP Headers

Free, instant results — no sign-up required.

Open HTTP Headers →
Similar Tools
Port Scanner SPF Lookup Subnet Calculator — IPv4 & IPv6 BIMI Lookup Traceroute DMARC Lookup DKIM Checker DNS Lookup
HTTP Headers Checker — Inspect Response Headers for Any URL Instantly
Internet
HTTP Headers Checker — Inspect Response Headers for Any URL Instantly
HTTP Security Headers: What Most Sites Are Missing and How to Fix It
Internet
HTTP Security Headers: What Most Sites Are Missing and How to Fix It
HTTP Caching Deep Dive: Cache-Control Directives, ETags, and Content Hashing Strategy
Internet
HTTP Caching Deep Dive: Cache-Control Directives, ETags, and Content Hashing Strategy
CORS Explained: Why It's a Server-Side Fix for a Browser-Side Error, and How to Debug It
Internet
CORS Explained: Why It's a Server-Side Fix for a Browser-Side Error, and How to Debug It
HTTP/2 and HTTP/3 Changed the Transport, Not the Headers — Here's What That Means in Practice
Internet
HTTP/2 and HTTP/3 Changed the Transport, Not the Headers — Here's What That Means in Practice
HSTS Doesn't Protect the First Connection — Why the Preload List Exists and How to Use It Safely
Internet
HSTS Doesn't Protect the First Connection — Why the Preload List Exists and How to Use It Safely