Browser Caching: HTTP Cache Headers and Service Workers

Browser caching explained: Cache-Control, ETags and revalidation, immutable hashed assets, service worker caching strategies and what never to cache.

Intermediate 3 min read · Updated

Browser caching stores copies of responses on the user's device so repeat visits load faster and use less data. It's controlled mainly by HTTP headers: Cache-Control says how long a response may be reused and by whom, and validators such as ETag let the browser cheaply check whether its copy is still current. Service workers add a programmable cache for offline support and custom strategies. Getting the headers right is one of the highest-impact, lowest-effort performance wins.

Cache-Control directives

DirectiveMeaning
max-age=NThe response is fresh for N seconds; reuse it without asking the server
s-maxage=NFreshness for shared caches such as CDNs, overriding max-age there
no-cacheMay be stored, but must be revalidated with the server before each use (not "don't cache")
no-storeDon't store at all; for sensitive responses
privateOnly the user's browser may cache it, not shared caches; for personalised responses
publicShared caches may store it
immutableThe content will never change at this URL; don't revalidate even on reload
stale-while-revalidate=NServe a stale copy for up to N seconds while fetching a fresh one in the background

Revalidation with ETags

When a cached response is stale, the browser asks the server whether it changed, sending its validator. If not, the server replies 304 Not Modified with no body, saving bandwidth.

BrowserServerGET /api/profile200 + ETag: "v42", Cache-Control: no-cachelater: copy must be revalidatedGET + If-None-Match: "v42"304 Not Modified (no body)
Conditional revalidation: the browser sends its ETag; the server returns 304 when nothing changed, so the cached body is reused.

A caching policy that works

ResourcePolicyWhy
JS, CSS, fonts and images with a content hash in the filename (app.3f9a1c.js)public, max-age=31536000, immutableA changed file gets a new name, so old ones can be cached forever
HTML pagesno-cache (with an ETag) or a short max-age at the CDNHTML references the current hashed assets, so it must stay fresh
Public API responsesShort max-age or s-maxage with stale-while-revalidateFast and mostly fresh; tune per endpoint
Personalised API responsesprivate, no-cache or no-storeNever let a shared cache serve one user's data to another
Sensitive data (banking, health)no-storeDon't leave copies on shared devices

Never give HTML a long max-age. If you do, users keep loading old HTML pointing at old assets, and a fix can't reach them until their cache expires.

Service worker caching

A service worker is a script that sits between the page and the network, intercepting requests. With the Cache Storage API it can implement strategies HTTP headers can't express, and it enables offline use and installable web apps.

StrategyHow it worksUse for
Cache firstServe from cache; go to network only on a missVersioned static assets, fonts
Network firstTry the network; fall back to cache when offline or slowHTML, frequently changing API data
Stale-while-revalidateServe from cache, update the cache from the network in the backgroundAvatars, non-critical API data
Network only / cache onlyBypass or rely solely on the cachePayments (network only); precached app shell (cache only)
  • Precache the app shell at install time for instant, offline-capable loads.
  • Version caches and delete old ones when the service worker activates, or users can be stuck on stale code.
  • Plan updates: a new service worker waits until all tabs using the old one close, unless you prompt users to refresh.
  • Pair with an outbox for writes made offline. See offline-first sync.

Other browser storage

Beyond HTTP and service worker caches, apps store data in IndexedDB (large, structured, asynchronous; the right place for offline data), localStorage (small, synchronous strings; fine for preferences, not for large data or secrets) and cookies (sent with every request; keep them small). Browsers can evict storage under pressure unless persistent storage is granted, so treat client storage as a cache that may disappear.

Where it’s used

Single-page apps

Hashed bundles cached for a year, HTML revalidated on each load, so deploys take effect immediately.

Progressive web apps

A service worker precaches the shell and serves content offline, syncing changes when online.

Content sites

CDN s-maxage with stale-while-revalidate keeps pages fast and nearly fresh.

In an interview

  • Cache hashed assets as immutable for a year; keep HTML fresh.
  • Explain no-cache vs no-store and ETag revalidation with 304.
  • Mark personalised responses private so CDNs never share them.
  • Choose service worker strategies per resource and version the caches.

Further reading