What is the practical effect on a shared cache such as a CDN when a response carries Vary: Cookie, and how does that differ from Vary: *? When is each defensible?
answer
- Cookie header ≈ unique per user
- Hit rate collapse + cache pollution
- Vary: * = never reuse
- Normalize to one bit: logged-in / anonymous
- Privacy = private/no-store, not Vary
basics
~20 sVary: Cookie makes the cache key include the whole cookie header, which is near-unique per user, so shared caching effectively stops. Vary: * forbids reuse outright. Both are blunt; for genuinely per-user data use Cache-Control: private or no-store instead.
solid answer
~50 s`Vary: Cookie` is technically correct when the body depends on session state, but operationally it is close to disabling the shared cache: cookie headers carry session IDs, analytics IDs and consent flags, so nearly every user — often every request — produces a distinct secondary key. The origin still takes the full load, and the cache fills with single-use objects that evict everything else. `Vary: *` is stronger and honest: it says selection depended on something not expressible in headers, so no stored response may ever be reused. It is a correctness statement, not a tuning knob. It matters that neither is a privacy control. A cache that ignores or mishandles `Vary` — and some intermediaries do — leaks one user's page to another. For per-user content the right tools are `Cache-Control: private` (browser only) or `no-store`. `Vary: Cookie` is defensible mainly for pages with two states, when you first normalize the cookie down to one bit such as logged-in or not.
code
http · 5 linesHTTP/1.1 200 OK
Content-Type: text/html
Cache-Control: max-age=600
Vary: Cookie
Set-Cookie: sid=8f3c1e...; HttpOnlygo deeper
Know that varying on Cookie means almost every user gets their own cache entry, so the shared cache stops helping.
Contrast the two directives precisely, and know that per-user responses belong under Cache-Control: private or no-store.
Diagnose the hit-rate collapse and cache pollution, propose cookie normalization to a derived low-cardinality header, and explain why Vary is not a confidentiality boundary.
Set the policy: which variation axes belong in the URL, which in the cache key, which are simply not shareable — and make it enforceable in the edge configuration rather than per-endpoint header habits.
## What each one means `Vary: Cookie` adds the entire `Cookie` request header to the secondary cache key. Two requests share a cached response only if their full cookie strings match byte for byte after normalization. `Vary: *` says the response was selected using information the cache cannot inspect — client IP, time of day, an A/B bucket computed server-side. RFC 9111 forbids reusing such a stored response for any subsequent request. It may still be stored (for example to satisfy a later conditional revalidation), but it can never be replayed. ## Why Vary: Cookie destroys a shared cache Real cookie headers look like `sid=8f3c...; _ga=GA1.2.1837...; consent=v2; ab=blue`. The session identifier alone is unique per user; the analytics identifier is unique per browser; consent and experiment cookies add more axes; and any of them can be rewritten mid-session. The result is a secondary key with cardinality on the order of your user count, sometimes higher. Consequences at the edge: - **Hit rate collapses.** Practically every request is a miss, so the origin sees the traffic it was supposed to be shielded from. - **Cache pollution.** Millions of single-use objects consume storage and evict genuinely shareable assets, so unrelated URLs get slower too. - **Misleading dashboards.** The CDN reports 'caching enabled' and the origin reports full load; the contradiction is only explained by the variant count. This is the mechanism behind the classic incident where an origin behind a CDN keeps falling over under load that the CDN was supposed to absorb: some framework middleware attached `Vary: Cookie` to every response because a session was touched. ## When Vary: Cookie is defensible When the representation genuinely has a small number of states and you normalize the header down to those states before it reaches the cache key. A typical edge rule inspects `Cookie`, and rewrites it to `session=1` or `session=0` — or better, copies a single derived value into a dedicated header like `X-Auth-State` and varies on that. Now the URL has two variants: the anonymous page (widely shareable, high hit rate) and the logged-in page (marked `private` or `no-store` and not shared at all). Without that normalization step, `Vary: Cookie` on a shared cache should be treated as a defect. ## When Vary: * is defensible When the selection input truly is invisible to the cache and you cannot restructure it: geo- or IP-derived content, server-side experiment assignment, per-request randomization. `Vary: *` is then the honest declaration that no reuse is safe. But if you reach for it, ask first whether the varying input can be lifted into the URL — `/pricing?country=de` — which restores shared caching completely and makes the variation visible in logs, dashboards and purges. Putting the axis in the path or query string is almost always better than putting it in `Vary`. ## Vary is not a privacy boundary The most dangerous misconception is that `Vary: Cookie` makes a response safe to store in a shared cache because 'only the same cookie can retrieve it'. That relies on every intermediary implementing `Vary` correctly. Some proxies ignore it, some normalize cookies aggressively, some CDN configurations strip request headers before the key is computed, and a misconfigured edge rule can drop `Cookie` from the key entirely — at which point one user's authenticated dashboard is served to the next visitor. The confidentiality controls are: - `Cache-Control: private` — may be stored by the user's own browser cache, never by a shared cache. - `Cache-Control: no-store` — must not be written to any cache. Use those for per-user data, and treat `Vary` purely as a correctness mechanism for negotiated variants that are safe to share. ## Practical checklist 1. Is the response per-user? Use `private`/`no-store`; do not rely on `Vary`. 2. Is it shareable but two-state? Normalize the cookie to a derived low-cardinality value and vary on that. 3. Is it selected by something invisible to the cache? Try to lift it into the URL; use `Vary: *` only if you truly cannot. 4. Audit what your framework adds by default — session middleware attaching `Vary: Cookie` everywhere is the usual culprit.
- A CDN reports a 95% cache hit rate but the origin still sees nearly full traffic. What would you check first?I would look at the cache key: hit rate is usually reported per stored object, so a high-cardinality secondary key can coexist with near-total origin load. I would check whether responses carry Vary: Cookie or Vary: User-Agent, count distinct variants for one hot URL, and look for session middleware adding Vary indiscriminately.
- How would you make a page cacheable that differs only between anonymous and logged-in users?Derive a single low-cardinality value at the edge — for example map the presence of a session cookie to X-Auth-State: anon or auth — and vary on that header instead of Cookie. Serve the anonymous variant with a shared max-age and the authenticated variant with Cache-Control: private or no-store, so only the shareable half is stored at the edge.
Varying on Cookie is like a lost-property office filing every umbrella under the owner's full name and address: perfectly precise, and nobody ever finds a shared one.
saying these in an interview costs you the question
- Treating Vary: Cookie as a security control that keeps users' pages apart
- Reading Vary: * as 'vary on all headers' rather than 'never reuse'
- Leaving framework-default Vary: Cookie on static or anonymous pages
- Assuming a high CDN hit-rate metric proves the origin is shielded
- Adding Vary instead of moving the varying input into the URL