beginner 2 min answer

A user renames their account. The settings page shows the new name at once. The dashboard still shows the old name after a full page navigation and corrects itself only after a hard reload or about 15 minutes. Two different caches are involved. Which is which and which one can application code not invalidate?

http-cachedata-layercache-controletaginvalidation
Show the full answer Hide the answer

The two caches

The application data-layer cache lives in memory inside the page. It is keyed by entity or query, it disappears on navigation, and your code can clear or update it after a mutation. That is why the settings page was right: it wrote the new name into the cache it owns.

The browser HTTP cache lives on disk, is shared across tabs and navigations, and is keyed by request URL plus whatever Vary says. It is governed entirely by the response headers the server sent. Application code cannot invalidate it, because when it holds a fresh entry the fetch call never reaches the network — there is no request to intercept and nothing to clear.

The mechanism

The dashboard's GET /api/me came back with Cache-Control: max-age=900. For 900 seconds the browser answers that request from disk. The data layer is empty after the navigation, asks for the resource, and is handed a 15-minute-old body. A hard reload sends Cache-Control: no-cache, which forces revalidation, which is why the page appears to fix itself when a user does the one thing support always suggests.

The decision rule

  • Authenticated JSON gets Cache-Control: no-store, or private, no-cache with an ETag. Revalidation costs one round trip — roughly 30-80 ms on a decent mobile network — and a 304 carries no body, so you keep most of the saving without a staleness window you cannot cancel.
  • Freshness windows belong on immutable things. A content-hashed bundle gets Cache-Control: max-age=31536000, immutable, because its URL changes when its content does.
  • A max-age on a per-user response is also a security bug the moment a shared cache is in the path: without private, a CDN can store one user's body under a URL another user requests.

The failure mode to remember

A third cache can outlive both: a service worker that precached the API response survives a hard reload entirely, so the fix becomes a new service worker deployment plus a skipWaiting call.

When this is the wrong answer

For a public, non-personalised endpoint — a product catalogue, a status feed — a short max-age at the HTTP layer is the cheapest cache you will ever get and the right first move. The rule only tightens when the response depends on who is asking.

Common weak answers

  • "Clear the client cache on mutation." Correct and insufficient: the next request is answered before it leaves the process.
  • "Add a cache-busting query parameter." It works by making a new cache key, so every caller must remember to do it, and the CDN hit rate goes to zero. Fix the header instead.