Cache invalidation at the edge — TTL, purge, surrogate keys. — Cracked Java
// High-Level Design (HLD / Distributed Systems) · CDN & Edge
SeniorSystem Design

Cache invalidation at the edge — TTL, purge, surrogate keys.

Cache invalidation at the edge — TTL, purge, surrogate keys

Edge invalidation is the same hard problem as application-cache invalidation, made harder by distribution: a cached object may live in hundreds of PoPs worldwide, so "delete this entry" means propagating a change across a global fleet. There are three mechanisms, used in combination.

The three mechanisms

MechanismHow it worksFreshnessCost
TTLEach object expires after a set time (Cache-Control: max-age, s-maxage)Bounded by the TTLFree, no coordination
PurgeExplicit API call invalidates a URL (or path/wildcard) across all PoPsNear-immediate (seconds)A purge call per change
Surrogate keysTag objects with keys; purge by key to drop all related objects at onceNear-immediate, groupedTagging discipline at the edge

TTL — the default

Set Cache-Control: max-age (browser) and s-maxage (shared/CDN) and let objects expire. It needs no coordination and is self-healing, but staleness is bounded only by the TTL. For static assets the standard trick is content-hashed URLs (app.3f9a2c.js) with a near-infinite TTL: a new deploy produces a new URL, so you never invalidate — you just stop referencing the old one. That makes TTL the right answer for immutable, versioned assets.

Purge — explicit and immediate

When content changes and you can't wait for the TTL (a corrected article, a pulled image), call the CDN's purge API to invalidate a specific URL across all PoPs within seconds. Purging works by URL, by path prefix, or with wildcards. The limitation: it operates on individual URLs, which is painful when one logical change affects many URLs (an updated product appearing on the homepage, a category page, search results, and an API endpoint).

Surrogate keys — purge by relationship

Surrogate keys (a.k.a. cache tags) solve the "one change, many URLs" problem. The origin attaches a Surrogate-Key header listing tags an object belongs to (e.g. product-123 category-shoes). Later, purging the tag product-123 evicts every cached object carrying it, no matter how many URLs that is, in a single call.

One surrogate-key purge evicts every object tagged with it

This is the senior answer for dynamic content: cache aggressively with long TTLs and tag everything with surrogate keys, then purge precisely by relationship on each write. You keep a high hit ratio without serving stale data.

Stale-while-revalidate

A complementary header: stale-while-revalidate lets the edge serve the slightly-stale cached copy immediately while it refreshes from origin in the background — so an expiring object never causes a blocking miss for the user, smoothing the edge equivalent of a cache stampede.

Mark your status