In HTTP caching, what is the difference between a private cache and a shared cache, and what do the Cache-Control response directives private and public each tell them?
answer
- private = browser only; shared = CDN/proxy serves many
- private is not secrecy - no-store is
- public overrides the Authorization block
- Set-Cookie plus shared cache = leaked session
- anonymous cacheable GET is already shareable
basics
~20 sA private cache serves one user - the browser's own store. A shared cache serves many - a CDN, proxy or gateway. Cache-Control: private forbids shared caches from storing the response; public explicitly permits storing even responses that would otherwise be ineligible, such as authenticated ones.
solid answer
~50 sA **private cache** belongs to a single user: the browser disk and memory cache. A **shared cache** sits in front of many users: a forward proxy, a reverse proxy or gateway, a CDN edge node. The distinction matters because a shared cache can serve one user's stored response to a different user. So personalised responses must never reach one. `Cache-Control: private` says exactly that: only a private cache may store this. It does not mean "do not cache" - the browser still may, which is often what you want for a user's own dashboard. `public` is the opposite signal. It says a shared cache may store this even when the normal rules would say no - most importantly, when the request carried an `Authorization` header, which otherwise makes the response ineligible for shared storage. For an ordinary anonymous, cacheable GET you rarely need `public`; freshness directives alone suffice. Neither directive protects secrets by itself: `no-store` is the one that forbids writing the response to any cache at all.
code
http · 6 linesHTTP/1.1 200 OK
Cache-Control: private, max-age=60
Vary: Cookie
Content-Type: application/json
{"user":"alice","unreadCount":3}go deeper
Name the two cache populations and give the one-line meaning of private and public.
Explain the Authorization default that public overrides, and that private still permits browser storage.
Talk about defaults leaking - unmarked responses, Set-Cookie at the edge - and insist on explicit directives per route.
Treat cacheability as part of the response contract: classify endpoints by principal-scope, enforce it in a shared layer, and audit it rather than leaving it to per-handler judgement.
## Two populations of cache HTTP caches split by who they serve. A **private cache** is dedicated to one user. The obvious example is the browser's HTTP cache; an HTTP client library's per-process cache counts too. Anything it stores can only ever be replayed to the same user. A **shared cache** stores responses on behalf of many users. This includes forward proxies inside a corporate network, reverse proxies and gateway caches in front of an origin (Varnish, nginx, an API gateway), and CDN edge nodes. A response it stores may be handed to a completely different requester later. That single fact drives every rule in this area: **a shared cache turns a personalised response into a data leak**. If a shared cache stores "Welcome back, Alice" under the key `GET /home`, the next anonymous requester gets Alice's page. ## The directives `Cache-Control: private` restricts storage to private caches. A shared cache must not store the response; the browser may. Use it for anything scoped to the logged-in user that is still worth caching locally - account pages, personalised feeds, per-user API responses. It is a *storage* rule, not a secrecy guarantee: it is a directive that well-behaved caches obey, and the response still travels the network. For genuinely sensitive material - a password reset page, a one-time token, banking detail - use `no-store`, which forbids any cache from writing it anywhere. `Cache-Control: public` marks a response as storable by a shared cache *even when it would otherwise not be*. The main case is authenticated requests: by default, when a request carries an `Authorization` header, a shared cache must not reuse the response for another request, precisely because the content is presumed to be principal-specific. `public` (or `s-maxage`, or `must-revalidate`) overrides that presumption. So `public` is a deliberate override, used when an endpoint requires a credential but returns the same bytes to everyone - a licensed asset, a shared reference document behind a login wall. For a plain anonymous GET with `Cache-Control: max-age=3600`, adding `public` changes nothing. Many teams add it out of habit; the real risk is adding it reflexively to an authenticated route and publishing one user's data to the edge. ## What a shared cache may store by default Absent explicit directives, a shared cache may store a response when the method is cacheable (GET or HEAD in practice), the status is one it understands as cacheable, the request had no `Authorization` header, and there is either an explicit freshness lifetime or a validator it can use. Cookies deserve special mention: a `Set-Cookie` on a response that a shared cache stores would replay one user's cookie to another. Most CDNs therefore refuse to cache responses carrying `Set-Cookie`, but this is product behaviour rather than a protocol requirement - never rely on it as your only safeguard. Mark such responses `private` or `no-store` explicitly. ## Getting it right A practical decision procedure for a response: 1. Does it contain anything specific to one principal? If yes, `private` at most, and `no-store` if it is sensitive. 2. Is it identical for every requester? Then it is a candidate for shared caching; give it an explicit lifetime. 3. Does the endpoint require an `Authorization` header even though the body is identical for everyone? Only then is `public` the right tool, and pair it with a check that no per-user field leaks into the body. The common production failure is a login-gated page that returns per-user content and is marked `public` for performance, or a route where framework defaults leave no directives at all and a CDN applies its own heuristic. Explicit directives on every response - even `no-store` - are cheaper than diagnosing a cross-user leak later.
- Does Cache-Control: private keep sensitive data safe?No. It only tells caches that shared storage is not allowed; the response still crosses the network and still lands on the user's disk in the browser cache. For material that must not be written down anywhere - reset links, one-time codes, financial detail - use no-store, and rely on TLS for the transport rather than on any cache directive.
- Why does an Authorization request header block shared caching by default, and how do you opt back in?Because a credentialed request is presumed to produce principal-specific content, so replaying it to another user would leak data. A response can opt back in by carrying public, s-maxage, or must-revalidate, which explicitly authorise shared storage. Only do that when the body is genuinely identical for every authorised caller.
A private cache is the copy of a letter you keep in your own desk; a shared cache is the pile on the office front desk that anyone walking past can pick up.
saying these in an interview costs you the question
- Believing Cache-Control: private means the response is not cached at all
- Adding public to authenticated per-user routes to improve hit rate
- Assuming a CDN will always refuse to cache a response carrying Set-Cookie
- Thinking public is required for ordinary anonymous responses to be cached
- Confusing private with no-store, or claiming either provides confidentiality