System Design Prepgo pro
Study guide 06 of 16

CDNs and edge computing

How a content delivery network works, push vs pull, cache keys and TTLs, what to put at the edge, edge functions and KV, and the pitfalls of caching dynamic content.

A content delivery network is a set of servers placed near users (points of presence, PoPs) that serve cached copies of your content so requests do not travel to your origin. For static assets it removes most of your bandwidth and 50 to 200 ms of latency. Increasingly it also runs code, which turns the edge into a first tier of your application.

How a request flows

  1. DNS resolves your hostname to the CDN (a CNAME to the provider, or the provider's anycast IP).
  2. The user's request lands at the nearest PoP.
  3. The PoP checks its cache using a cache key (by default the URL, optionally plus headers such as Accept-Encoding or a cookie).
  4. On a hit, the PoP responds. On a miss, it fetches from the origin (or from a regional "shield" cache that many PoPs share, so the origin sees one miss instead of hundreds), stores the response according to its Cache-Control headers, and responds.

Origin shielding is worth naming: without it a global cache expiry produces one origin request per PoP, and a large provider has several hundred PoPs.

Pull versus push

Pull (origin pull) is the default: the CDN fetches from your origin on the first miss. No upload step, content is always available at the origin, and only requested content is cached. The first request in each PoP is slow.

Push means you upload content to the CDN ahead of time. Used for large files with predictable demand (a game patch, a video release) so the first user in every region gets a hit. Most systems pull; say push only for pre-warming a known launch.

What to cache and for how long

  • Immutable assets (JS, CSS, images with a content hash in the filename): Cache-Control: public, max-age=31536000, immutable. Cache forever; a new deploy has new filenames.
  • Public pages and API responses that are the same for everyone: short TTLs (10 s to a few minutes) with s-maxage for the CDN and max-age for browsers, plus stale-while-revalidate so a user never waits for the refresh.
  • Personalised responses: normally not cached at the CDN. If you must, the cache key has to include the user (a cookie or header), which destroys the hit rate. Better: cache the shell publicly and fetch the personal part separately.
  • Large media: always. Object storage as origin, CDN in front, signed URLs for private content.

Use Vary sparingly. Every header in Vary multiplies the cache entries; Vary: Cookie effectively disables caching.

Invalidation

CDNs offer purge by URL, by prefix, or by tag (surrogate key). Tag-based purge is the useful one: tag every response with the entity ids it contains (product-123, category-9), and when the product changes, purge the tag. Propagation to all PoPs takes seconds. For content that changes on a schedule, prefer a short TTL over purging: it is simpler and self-healing.

Versioned URLs avoid invalidation entirely: change the filename, and the old entry is simply never requested again.

Edge compute and edge storage

Providers now run functions at the PoP (Cloudflare Workers, Fastly Compute, Lambda@Edge, Vercel Edge Functions), with a replicated key-value store next to them (Workers KV, Fastly Config Store, Vercel Edge Config). Latency to that KV is single-digit milliseconds, and it replicates globally in seconds to a minute.

Good uses, each of which comes up in interviews:

  • Redirects and routing. A URL shortener serving code → URL from edge KV never touches the origin for the read path. See Design a URL Shortener.
  • Authentication and feature flags. Validate a JWT or read a flag at the edge and route accordingly.
  • A/B assignment and personalised caching. Decide the variant at the edge, then serve a cached variant page.
  • Rate limiting and bot filtering before traffic reaches the origin.
  • Response assembly. Fetch a cached shell and a small dynamic fragment and stitch them.

The constraints: no persistent connections to your database (or only through a regional proxy), limited CPU time per request, and the KV is eventually consistent, so it holds data that can be a minute stale, not the source of truth.

Failure modes to mention

  • Origin overload on expiry: many PoPs miss at once. Shielding, stale-while-revalidate, and staggered TTLs (jitter) address it.
  • Caching an error: a 500 gets cached and served for the TTL. Configure short or zero TTLs for error responses and stale-if-error to serve the last good copy.
  • Cache poisoning: an attacker gets a response with malicious content or a bad header cached under a public key. Keep the cache key strict, and never include unvalidated request headers in the response.
  • Private data at the edge: a personalised response cached publicly leaks one user's data to another. Set Cache-Control: private on anything user-specific.

Interview phrasing

"Static assets and media go through the CDN with immutable, hashed URLs. Public API reads get a 30-second s-maxage with stale-while-revalidate, tagged by entity so we can purge on write. Personalised responses bypass the CDN. For the redirect path I would run an edge function reading from edge KV, so p99 is under 20 ms globally and the origin only sees misses."