Filter concepts by levelShowing all levels.

System Design · CDN and Edge Caching

CDN and Edge Caching

Caching near the user, the headers that control it, and telling public content apart from private

  • Edge locationscaching near the user, not the origin
  • Cache-Controlmax-age, s-maxage, private, no-store
  • Signed URLsrestrict access to cached content
  • Public vs privateidentical-for-everyone is the test

1.CDN caching for static assets and cacheable responses

A CDN (content delivery network) is a globally distributed set of caching servers, positioned close to end users geographically, that cache and serve content on behalf of an origin server. It removes both the network distance and the origin's repeated-serving cost for anything that can be safely cached — classically images, CSS and JS, but also API or page responses that are the same for many users.

Key features

  • Origin server: the source of truth the CDN caches from — the CDN is a caching layer in front of it, not a replacement for it
  • Edge location (PoP — point of presence): a CDN's server, geographically distributed, that serves cached content to nearby users
  • Static assets — images, CSS, JS, video, downloadable files — are the classic, easiest CDN caching target since they rarely change per-request
  • Dynamic but cacheable content — an API response or rendered page that is identical for many users for a bounded time — can also be CDN-cached, not just static files
  • A CDN reduces both latency (serving from a nearby edge instead of a distant origin) and origin load (the origin is only hit on a cache miss)

What typically is and isn't a good CDN caching candidate

What typically is and isn't a good CDN caching candidate
ContentGood CDN fitWhy
Images, CSS, JS bundlesYesIdentical for every user, changes rarely, versioned by filename
A public blog post pageYesSame content for every visitor, changes infrequently
A logged-in user's dashboardNo (without care)Different content per user — caching it risks leaking one user's data to another
A public product listing API responseOften yes, with a short TTLSame for all users, tolerates brief staleness
CDN edge cache: hit vs. miss
requestcachedmiss: fetch+ cache

Client

e.g. Tokyo

CDN edge

nearby PoP

Hit: ~5ms

served from edge

Origin

us-east, single region

  • Client — e.g. Tokyo
    • leads to CDN edge (request)
  • CDN edge — nearby PoP
    • leads to Hit: ~5ms (cached)
    • leads to Origin (miss: fetch + cache)
  • Hit: ~5ms — served from edge
  • Origin — us-east, single region

Command

text
Client → CDN edge (near client)
           ├─ cache hit  → serve from edge, fast
           └─ cache miss → fetch from origin,
                            cache it, then serve

A CDN caches content at edge locations near users, cutting both network latency and origin load — for static assets by default, and for dynamic content when it is explicitly marked cacheable and does not vary per user.

2.Cache-Control headers, TTLs, invalidation and signed URLs

Cache-Control headers are the explicit contract between origin and CDN about what can be cached, for how long, and by whom — max-age sets freshness duration, s-maxage overrides it specifically for shared caches like CDNs, private/public control whether a shared cache may store the response at all. Invalidation purges already-cached content before its TTL expires, and signed URLs restrict access to cached content to authorized requests only.

Key features

  • max-age=N: response is fresh for N seconds, applies to both browser and CDN caches; s-maxage=N overrides it specifically for shared caches like CDNs
  • private: only a browser (private) cache may store this — a CDN or other shared cache must not; public explicitly allows shared caching
  • no-store: never cache this response anywhere; no-cache: may store it, but must revalidate with the origin before reuse
  • CDN invalidation (purge): explicitly evicts a cached object before its TTL expires, needed when content changes and cannot wait out the TTL
  • Signed URLs/cookies: a time-limited, cryptographically signed link that lets a CDN serve otherwise-private content only to requests carrying a valid signature

Common Cache-Control directives for CDN use

Common Cache-Control directives for CDN use
DirectiveEffectTypical use
max-age=3600Fresh for 1 hour, browser + CDNContent that changes hourly at most
s-maxage=3600CDN-specific TTL, overrides max-age for shared cachesDifferent freshness policy for CDN vs browser
privateOnly the browser may cache; CDN must notPer-user personalized responses
no-storeNever cached anywhereSensitive data that must always hit the origin
must-revalidateOnce stale, must check with origin before reuseContent where serving stale after expiry is unacceptable
Deploying a new logo without waiting out the TTL
Origin
CDN Edge
Browser
  1. 1logo.png, max-age=86400
  2. 2serves cached logo (old)
  3. 3purge /logo.pngdeploy triggers invalidation
  4. 4re-fetch on next request
  5. 5serves fresh logo
  1. Origin → CDN Edge: logo.png, max-age=86400
  2. CDN Edge → Browser: serves cached logo (old)
  3. Origin → CDN Edge: purge /logo.png (deploy triggers invalidation)
  4. CDN Edge → Origin: re-fetch on next request
  5. CDN Edge → Browser: serves fresh logo

Command

http
Cache-Control: public, max-age=300, s-maxage=3600
-- browser caches 5 minutes, CDN caches 1 hour

Cache-Control: private, no-store
-- never cached by any shared cache (CDN, proxy)

max-age sets freshness, s-maxage overrides it for shared caches, private/no-store keep content out of shared caches. TTL alone bounds staleness — an actual content change needs explicit invalidation or filename versioning to propagate immediately. Signed URLs restrict cached content to authorized requests.

3.Identifying content that is safe to cache vs user-specific content

Deciding whether a given response is safe to cache at a shared CDN edge comes down to one question: is this response identical for every requester who could plausibly ask for it? If yes, it is generally safe to cache publicly. If the response depends on who is asking — their identity, permissions, or personalized data — caching it at a shared edge without extra care risks serving one person's private response to someone else.

Key features

  • Public/shared-safe: the same response for any requester — a public product page, a static asset, a public API listing
  • Private/user-specific: differs by requester — an account dashboard, a personalized feed, anything gated by authentication
  • The Vary header tells a cache which request headers (like Authorization or a session cookie) make a response vary, so it caches separate copies per distinct value instead of one shared copy
  • A response can look public but leak private data indirectly — an error message that includes account details, or a response that differs subtly based on permissions
  • When in doubt, default to non-cacheable (private/no-store) — the cost of a missed caching opportunity is far lower than the cost of a cross-user data leak

Deciding cacheability by content type

Deciding cacheability by content type
ContentCacheable at a shared edge?Reasoning
Public marketing pageYesIdentical for every visitor
Product catalog API (no auth)Yes, with a TTLSame data for all callers, tolerates brief staleness
"Your recent orders" pageNo — or only with per-user cache keysDifferent content depending on who is logged in
Search results for a public query stringOften yes, keyed by the query itselfSame results for anyone running the same search
A permission-gated admin reportNoAccess itself must be checked per request, not assumed from a cached copy
Safe to cache at a shared edge?

Cacheable

  • Identical response for any requester
  • Public marketing page, static asset
  • Product catalog API with no auth

Not cacheable (as-is)

  • Differs by who is asking
  • Account dashboard, personalized feed
  • Even one embedded personalized field taints the whole response
  • Cacheable
    • Identical response for any requester
    • Public marketing page, static asset
    • Product catalog API with no auth
  • Not cacheable (as-is)
    • Differs by who is asking
    • Account dashboard, personalized feed
    • Even one embedded personalized field taints the whole response

Signature

safe to cache = identical for every requester, with no embedded personalized fields

Cache only what is truly identical for every requester. A response that is "mostly" shared but contains even one personalized field is not safe to cache as a whole — split it or mark the whole thing private.

Remember

  • A CDN cuts both network latency and origin load, but only for content explicitly marked cacheable.
  • s-maxage lets an origin set a different TTL for shared caches (CDNs) than for the browser.
  • TTL alone bounds staleness — an urgent content change needs explicit invalidation or filename versioning.
  • A response with even one embedded personalized field is not safe to cache as a whole.

Pro tip

  • Version static asset filenames instead of relying on invalidation — it sidesteps propagation delay entirely.
  • Default to private/no-store when uncertain — a missed cache hit costs far less than a cross-user data leak.

Next up

  • Messaging and queues
  • Rate limiting
  • Observability

Sources

  • MDN Cache-Control / Vary reference — developer.mozilla.org, fetched 2026-08-21
  • Checked 2026-08-21

Spotted a mistake on this page? Tell us.