Tags: web-dev concept

Caching Strategies

Date: 2026-08-17


Keeping a copy somewhere faster to reach. Every layer of a web stack has one, and the hard half is never storing the copy — it’s knowing when the copy is wrong, and having a plan for that before it happens.


A cache stores the result of an expensive operation so subsequent requests can skip it. Invalidation is removing or refreshing a copy that no longer matches the truth.

Where copies live

browser memory       this page view
browser disk         across visits
service worker       programmable, offline
CDN edge             per region
reverse proxy        in front of the app
application memory   per process
shared cache         Redis — across
                     processes
database buffer      the database's own

A single request may pass eight caches. When something stale appears on screen, the diagnostic question is which one — and the answer is usually found by working inward from the browser.

The patterns

CACHE-ASIDE (lazy)
  read → miss → fetch → store → return
  most common; first request is slow;
  cache only holds what's asked for

READ-THROUGH
  the cache fetches on miss itself
  same behaviour, hidden in the layer

WRITE-THROUGH
  write to cache AND store together
  cache always fresh; writes slower

WRITE-BEHIND
  write to cache, persist later
  fast writes, risk of loss on crash

REFRESH-AHEAD
  refresh popular entries before expiry
  no stale, no miss penalty, more work

Cache-aside is the default and the one to assume unless told otherwise.

The invalidation approaches

TTL — expire after a fixed time
  simple, predictable, ALWAYS somewhat
  stale. Fine for most content

EXPLICIT PURGE — evict on change
  accurate; needs a reliable hook from
  whatever changes the data; a missed
  purge is invisible

VERSIONED KEYS — never invalidate
  /app.a3f9c.js
  content changes → the KEY changes
  → old entry is simply never requested
  ← no staleness problem at all

Versioned keys are the only approach without an invalidation problem, which is why every build tool hashes filenames. Use it wherever you control the URL.

stale-while-revalidate

The pattern worth knowing by name, because it removes the usual trade:

Cache-Control: max-age=60,
  stale-while-revalidate=600

0–60s     fresh → serve from cache
60–660s   stale → SERVE IT ANYWAY,
                  refresh in background
>660s     serve nothing stale; fetch

Nobody ever waits for a refetch. The cost is that content can be up to a minute behind, which for a product listing is almost always the right trade — Content Delivery Networks.

What not to cache

personalised HTML   basket, account,
                    "hello, name"
responses with
  Set-Cookie        distributes one session
                    to many users
anything behind
  authorisation     unless keyed per user
prices, if they
  vary by customer

See: Dynamic Pricing

The catastrophic version is caching a personalised page at a shared layer — one user’s basket served to everyone. Cache-Control: private restricts a response to the browser cache only, and it is the flag that prevents this.

Cache stampede

When a popular entry expires and every concurrent request misses at once:

1,000 requests/second
key expires
→ 1,000 simultaneous origin requests
→ origin falls over
→ nothing repopulates the cache
→ it stays down

Fixes: a lock so only one request rebuilds, stale-while-revalidate so nobody waits, or jittered TTLs so keys don’t expire in lockstep. The last is the cheapest and catches most of it.

Measuring

HIT RATIO       by content type. Low on
                static assets is a config
                bug, not a tuning problem
LATENCY         hit vs miss — quantifies
                the value
EVICTION RATE   high means the cache is
                too small
STALENESS       how far behind, and does
                anyone notice

The rule to carry

Decide the invalidation strategy before adding the cache. A cache added for speed with no answer to “how does this get updated” becomes a source of bugs that present as data problems and get investigated as data problems — and the layer responsible is the last place anyone looks.