Tech

HTTP Caching Without Serving the Wrong Response

A practical way to separate browser and shared caches, choose Cache-Control directives, define cache keys and verify that faster delivery does not cross a user or content boundary.

By Jay Jung · Reviewed 11 September 2026
A browser cache and shared CDN cache leading to an origin with separate rules for public, personalised and varied responses

A cache is safe only when reuse is safe

  1. 1 · Classify

    Decide who may receive the same response

    A private cache belongs to one user agent. A shared cache, such as a CDN, can reuse one stored response for many users. Make that boundary explicit before choosing a lifetime.[1][2]

  2. 2 · Key

    Name every input that changes the representation

    The request method and target URI form the minimum cache key. When a response also changes with a request header, Vary tells a cache to include that header when selecting a stored representation.[1][4]

  3. 3 · Control

    Set storage and freshness deliberately

    Cache-Control separates storage from reuse. no-store prevents storage, no-cache permits storage but requires validation before reuse, private excludes shared caches, and s-maxage controls freshness in shared caches.[1][3]

  4. 4 · Prove

    Test the response matrix, not one happy request

    A cache can store redirects and negative results such as 404 responses as well as successful pages. Verification needs repeated requests across user state, language, query and error conditions, followed by a purge and recovery test.[1]

The first question is who can share the bytes

Teams often begin with a time-to-live because it is easy to measure. That is the second decision. The first is whether two requests are allowed to receive the same representation. Public documentation may be safely shared. An account page, draft preview or response containing a session-specific token usually may not be.[1][2][3]

Cookies do not make a response private by themselves. If authenticated or personalised content may be stored in a browser, say private. If the response should not be stored at all, say no-store. Do not rely on a CDN rule to infer the privacy boundary from whichever headers happen to be present.[1][2][3]

A cache key is part of application correctness

Most caches begin with the method and URL. That is enough only when the URL identifies one reusable representation. If the response changes by language, accepted encoding or another request header, the key must reflect that input. Vary is the HTTP mechanism for declaring those header-driven variants.[1][4]

Avoid using Vary as a substitute for a clear URL or privacy rule. Vary: User-Agent creates many low-reuse variants, while Vary: Cookie can multiply the key space and still fail to describe which cookie matters. Locale paths, versioned assets and private responses usually make the boundary easier to operate.[1][4]

no-cache does not mean do not store

The naming causes expensive mistakes. no-cache allows a response to be stored, but requires the cache to validate it before reuse. no-store tells caches not to store it. private permits storage in a user's cache but keeps the response out of shared caches. s-maxage sets a freshness lifetime for shared caches without changing the browser's max-age.[2][3]

For hashed JavaScript, CSS and image files, a long lifetime works because a content change produces a new URL. Public HTML usually benefits from validation with an ETag or Last-Modified value. Personalised pages need a private or no-store policy before any performance tuning begins.[2][3]

Test the states that can poison the next request

A successful anonymous request proves very little. Repeat the same URL before and after login, across supported locales, with query variants, and after the origin returns an error. Inspect Age, Cache-Control, Vary, validators and the CDN's cache-status header alongside the body and status code.[1][2]

Include recovery in the release test. Purge the exact key, confirm the next request reaches the intended origin path, and check that a 404 or redirect cannot be stored under the canonical page's identity. The useful result is not a fast first demonstration; it is predictable reuse under ordinary failures.[1][2]

  • List public, private and uncacheable response classes
  • Define the complete key for each shared response
  • Check anonymous and authenticated requests separately
  • Probe locale, query, redirect and 404 variants
  • Exercise targeted purge and origin recovery

HTTP standard and implementation guidance

  1. RFC 9111, HTTP Caching
  2. MDN Web Docs, HTTP caching
  3. MDN Web Docs, Cache-Control header
  4. MDN Web Docs, Vary header

Bring us the complicated part.

A useful first conversation is enough to define the problem and the next decision.

Start a conversation