Tags: web-dev concept

CDN Caching

Date: 2026-08-16


Copies of your responses held close to users, so requests don’t cross the world to your origin. The hard parts aren’t the caching — they’re the cache key and the invalidation, and both fail quietly.


What it is

A content delivery network (CDN) is a distributed set of edge servers that store and serve copies of your responses. A request is answered by the nearest edge holding a valid copy; only a miss reaches your origin.

Two benefits, and the second is usually the bigger one:

  • Distance. Round trips are physics — an edge 20ms away beats an origin 200ms away — see Latency and Bandwidth
  • Origin offload. A high hit rate means your servers handle a fraction of traffic, which is capacity and cost as well as speed

The cache key

The single most important concept, and the one that causes the most surprises.

The cache key determines what counts as “the same request”. By default it’s usually the URL, but everything included in it multiplies the number of stored copies.

key = URL only
  /product/socks                    →  1 cached object, high hit rate

key = URL + all query strings
  /product/socks
  /product/socks?utm_source=email   →  separate objects for every campaign
  /product/socks?fbclid=xyz123          → hit rate collapses

key = URL + cookie
  → one cached object per user      →  caching effectively disabled

Tracking parameters are the classic destroyer of hit rate. Every utm_*, gclid and fbclid variant creates a distinct cached object for content that is byte-identical. The fix is to strip marketing parameters from the cache key while still passing them to the page — most CDNs support an ignore-list, and it’s often the single highest-value CDN setting on a retail site.

Same for cookies: many CDNs skip caching entirely for requests carrying cookies. One analytics cookie set on / can disable caching site-wide.

s-maxage and shared caches

Cache-Control: public, max-age=0, s-maxage=300, stale-while-revalidate=86400
  • max-age=0 — browsers always check
  • s-maxage=300 — the CDN may serve it for five minutes without touching the origin
  • stale-while-revalidate — after that, serve stale immediately and refresh behind

This combination is what makes HTML cacheable at the edge while staying fresh in the browser, and it’s the setting that most improves Time to First Byte — see HTTP Caching.

Invalidation

The genuinely hard part, and there are only three approaches:

ApproachWorks forCost
Short TTLContent that tolerates being minutes staleLower hit rate, more origin load
Purge on publishContent with a clear change eventPurge latency and API complexity
Fingerprinted URLsStatic assetsNone — the URL changes, so nothing to purge

Fingerprinting is the only one with no failure mode, which is why the pattern is universal for JS, CSS and images. Anything that can be fingerprinted should be.

For HTML, tag-based purging — where responses carry surrogate keys like product-4471 and a publish purges by tag — is the workable approach on commerce platforms with thousands of pages sharing components.

Personalisation vs caching

The tension that decides architecture on most retail sites.

Any per-user content in the HTML — a name, a basket count, personalised recommendations — makes the whole page uncacheable. The usual resolutions:

  • Cache the shell, fetch the personal parts client-side. Simple, and it means the personal bits arrive late
  • Edge-side personalisation — the CDN assembles cached fragments with fresh ones — Edge Computing
  • Vary the cache key on a segment, not on the user. Ten segments is ten cached copies; ten million users is no caching
  • Accept a lower hit rate on pages where personalisation genuinely earns more than speed costs

Watching it

  • Hit rate, overall and by path. Below 80% for static assets means something is wrong with the key
  • x-cache / cf-cache-status response headers tell you HIT, MISS, EXPIRED or BYPASS per request. BYPASS on a page you expected to cache usually means a cookie
  • Origin request volume. A rise with flat traffic means hit rate has dropped, often after a change to how parameters or cookies are handled
  • Segment field data by country. A misconfigured region shows up as a slow tail nowhere else — Percentiles in Performance

Traps

  • Caching a personalised page. The worst possible failure: one user’s basket or account details served to another. private and no-store exist for this
  • Missing Vary: Accept-Encoding, so a Brotli response reaches a client that can’t read it
  • Purging everything on every deploy, which empties the cache and hammers the origin at exactly the moment the new code is least proven
  • Assuming a purge is instant. Propagation across a global network takes time, and it’s rarely zero