System Design

CDN

A Content Delivery Network is a globally distributed set of edge servers that cache content close to users, cutting latency and offloading the origin.

Content near the user.

If your server is in Virginia and a user is in Tokyo, every request crosses the globe — slow. A CDN places copies of your content on edge servers in many cities. The Tokyo user is served from a nearby edge instead of Virginia.

CDNs are ideal for static assets: images, CSS, JavaScript, videos, and downloads. Benefits:

  • Lower latency — shorter physical distance.
  • Less origin load — the edge answers most requests.
  • Resilience & scale — absorbs traffic spikes and some attacks.

Push vs pull.

Pull CDN: the edge fetches from your origin on the first request (miss), caches it, and serves subsequent requests. Push CDN: you upload content to the CDN ahead of time. Pull is easier and self-managing; push suits large, infrequently changing files.

  • TTL and Cache-Control headers control how long the edge caches an object.
  • Cache key — usually the URL; be careful with query strings and headers so you don't fragment or poison the cache.
  • Invalidation / purge — force the edge to drop stale content when you deploy.
Cache busting Put a version/hash in asset filenames (app.9f3a.js) so a new deploy is a new URL — you never fight cache invalidation.

Dynamic acceleration.

  • Dynamic acceleration — even uncacheable content benefits via optimized routing, TLS termination at the edge, and connection reuse to the origin.
  • Invalidation at scale — purging across thousands of edges is eventually consistent; versioned URLs sidestep it.
  • Edge compute — run code at the edge (Cloudflare Workers, Lambda@Edge) for personalization, A/B tests, auth, and header rewrites near the user.
  • Tiered caching — regional mid-tier caches shield the origin from many edges missing at once (origin shield).
Cache poisoning If unkeyed request inputs affect the response, an attacker can cache a malicious variant served to others. Key the cache carefully and normalize inputs.

Interview Questions

Filter

A Content Delivery Network — a geographically distributed network of edge servers that cache and serve content from locations close to users, reducing latency and origin load.

Static, cacheable assets: images, videos, CSS, JavaScript, fonts, and file downloads. These change infrequently and are identical for all users, making them ideal to cache at the edge.

A CDN cache server located in a Point of Presence — a data center geographically near end users. It serves cached content locally so requests don't travel to the distant origin.

The authoritative source of the content — your own servers/storage — from which the CDN fetches content to cache. The CDN sits in front of the origin and shields it from most traffic.

  • Lower latency for users via proximity.
  • Reduced origin load and better scalability, since the edge absorbs most requests and traffic spikes.

Cloudflare, Akamai, Amazon CloudFront, Fastly, and Google Cloud CDN.

Time-to-live: how long an edge server keeps a cached object before considering it stale and revalidating/refetching from the origin. It's usually set via Cache-Control: max-age.

Pull: the CDN fetches content from your origin lazily on the first request (cache miss), then caches it; you just point the CDN at your origin. Push: you proactively upload content to the CDN's storage. Pull is low-maintenance and self-updating (good for frequently changing sites), while push gives control and suits large files or infrequent traffic where you don't want the first user to pay the miss latency.

Options: set short TTLs (auto-expire), issue an explicit purge/invalidation via the CDN API/dashboard, or use versioned/fingerprinted URLs (app.v2.js or a content hash) so updated content is a new URL that can't be stale. Versioned URLs are preferred because purges are slower, eventually consistent across edges, and sometimes rate-limited.

Typically via anycast (the same IP announced from many PoPs, so BGP routes users to the nearest) and/or DNS-based routing (the CDN's DNS returns an edge IP based on the resolver's location and current load/health). It factors in geographic proximity, network conditions, and edge capacity to pick a fast, healthy PoP.

Fully personalized responses generally can't be cached, but CDNs still help: they terminate TLS at the edge, reuse warm connections to the origin, and use optimized backbone routing (dynamic acceleration) to speed uncacheable requests. Edge compute can generate/personalize responses at the edge, and you can cache fragments (ESI) or cache per-segment (e.g. per country/device) using cache-key variations.

The cache key is what the CDN uses to identify a cached object — usually the URL, sometimes including query strings, headers, or cookies. It matters because too broad a key (ignoring a meaningful query param) serves wrong content, while too narrow a key (keying on a unique param like a tracking ID) fragments the cache and kills hit ratio. You configure which inputs are part of the key and normalize them.

Its massive distributed capacity absorbs volumetric floods across many PoPs (via anycast) instead of one origin choking. It hides the origin IP, filters malicious traffic at the edge (WAF, rate limiting, bot detection), and serves cached content so attack traffic never reaches your servers. Many CDNs bundle dedicated DDoS scrubbing. It's a first line of defense, not a complete solution.

It's the HTTP header controlling caching behavior. Directives: max-age (TTL), public/private (cacheable by shared caches or only the browser), no-store/no-cache (don't cache / revalidate before use), s-maxage (TTL specifically for shared caches like CDNs), and stale-while-revalidate/stale-if-error. It lets the origin dictate how edges and browsers cache each response.

Cache poisoning is when an attacker gets a harmful response cached at the edge and served to other users. It happens when request inputs that influence the response (headers like X-Forwarded-Host, unkeyed query params) are reflected into cached content but aren't part of the cache key. Prevention: include all response-affecting inputs in the cache key (or strip/normalize them), avoid reflecting untrusted headers into responses, don't cache responses that vary on unkeyed inputs, validate and canonicalize inputs, and use Vary correctly. Regularly audit which headers/params affect responses.

When a popular object expires or isn't yet cached, many edges simultaneously miss and all fetch from the origin, spiking origin load (a stampede). Mitigations: request coalescing at each edge (one origin fetch per object while others wait), tiered caching / origin shield (edges pull from a designated regional mid-tier cache that consolidates requests to the origin), stale-while-revalidate to serve stale during refetch, and jittered TTLs so many objects don't expire at once. Origin shield can reduce origin traffic by orders of magnitude for global fan-out.

Use HTTP adaptive streaming (HLS/DASH): segment the stream into small chunks with a manifest, so each segment is a cacheable static object served by the CDN — millions of viewers hit edges, not the origin. Employ tiered caching/origin shield to protect the packager/origin, and pre-warm edges before the event. Keep segment TTLs aligned to segment duration and cache the manifest briefly. Use multiple CDNs with client-side CDN switching for capacity and resilience, anycast for routing, and edge logic for token-based access control/DRM. For ultra-low latency, use LL-HLS/CMAF chunked transfer. Plan capacity for the peak concurrent audience and monitor per-PoP hit ratio and rebuffering.

Edge compute (Cloudflare Workers, Lambda@Edge, Fastly Compute) runs code in the CDN PoPs, moving logic close to users: auth/token validation, A/B testing and feature flags, request/response rewriting, personalization, redirects, and API aggregation — all with sub-millisecond proximity and without a round trip to the origin. It enables patterns like edge-side rendering and edge caching of personalized fragments. Constraints: limited execution time/memory, cold-start and runtime limits (often V8 isolates/WASM), eventual consistency of any edge state (edge KV stores are eventually consistent), and debugging/observability across many PoPs. It blurs the line between CDN and application tier.

Hit ratio drives both performance and cost (origin egress/compute and CDN pricing often reward hits). Improve it by: normalizing cache keys (strip irrelevant query params, sort params, ignore tracking cookies), setting appropriately long TTLs with versioned URLs for busting, serving Vary only on meaningful dimensions, using tiered caching so mid-tiers keep objects warm, and segmenting content so cacheable and personalized parts are separate. Monitor hit ratio per PoP and per content type; a low ratio usually means over-fragmented keys, too-short TTLs, or too much uncacheable content. Higher hit ratio = lower latency, less origin cost, and better resilience.

A purge must propagate to thousands of edges that can't be updated atomically, so invalidation is eventually consistent — different users may briefly see old or new content, and purges can take seconds and may be rate-limited. This makes coordinated updates (e.g. a legally-required takedown or a breaking API change) tricky. Strategies: prefer versioned/immutable URLs so you never purge (deploy = new URL, instantly consistent), use short TTLs for content that must change fast, use soft-purge (mark stale, revalidate) to avoid origin stampedes on hard-purge, and design content so correctness doesn't depend on instant global purge. For critical invalidations, combine purge with a version bump.

Never cache private content in a shared cache without care. Approaches: mark responses Cache-Control: private, no-store so edges don't cache them; use signed URLs/cookies so the CDN serves protected objects only to authorized users for a limited time (great for private media/downloads); validate tokens at the edge with edge compute; and separate truly static shared assets (cacheable) from user-specific data (bypass or short private cache). For per-user but cacheable content, key the cache by user/segment carefully. Always ensure a misconfiguration can't cause one user's private response to be served to another (the classic dangerous cache bug).

Reasons: resilience (survive a single CDN's outage), performance (different CDNs are faster in different regions), cost negotiation/optimization, and capacity for huge events. Implementation: use DNS-based or client-side steering to select a CDN per request based on real-user-measurement (RUM) performance data, health, and cost, with automatic failover if one degrades. Challenges: keeping cache/config/purge consistent across CDNs, unified logging/observability, TLS cert management, and increased operational complexity. A CDN load balancer / traffic-management service (or DNS provider with RUM steering) typically orchestrates this.

A hit is when the requested object is already cached on the nearby edge server and served instantly. A miss means the edge must fetch it from the origin (or an upstream tier) first, then cache it, so the first user pays extra latency.

Not necessarily. You typically point your existing domain (or a subdomain like cdn.example.com) at the CDN via DNS (CNAME), so URLs stay the same but resolve to edge servers. Some setups serve assets from a dedicated CDN hostname.

Vary tells caches that the response differs based on certain request headers (e.g. Vary: Accept-Encoding for gzip vs brotli, or Accept-Language). The CDN then stores and serves separate cached variants per header value. Overusing Vary (e.g. on User-Agent) fragments the cache into many variants and destroys hit ratio, so use it only for dimensions that genuinely change the response.

The CDN terminates TLS at the edge, so it needs a certificate for your domain — either one you upload or (commonly) an auto-provisioned/managed certificate (e.g. via Let's Encrypt). It then connects to your origin over its own TLS. This offloads handshake cost from your origin and lets the edge serve HTTPS close to users. SNI lets one edge IP serve many domains' certs.

Pre-warming proactively loads content into edge caches before a known traffic spike (a launch or live event) so early users don't all miss and stampede the origin. Prefetching fetches likely-next resources ahead of a user's request (e.g. next video segments or linked pages) to hide latency. Both trade some wasted fetches for better perceived performance and origin protection.

Cache hit ratio (per PoP and content type), edge and origin latency, origin offload / origin request rate, error rates (4xx/5xx) by edge, bandwidth/egress, and geographic performance (real-user monitoring). Watch hit ratio drops (mis-keyed cache or short TTLs), rising origin traffic (stampedes or purge storms), and per-region latency to catch routing/PoP issues.

Add only coarse, low-cardinality dimensions to the cache key (e.g. country or device-class: mobile/desktop) rather than raw User-Agent or precise geo, so you get a small number of cached variants instead of thousands. Use edge compute to classify the request into a bucket and either rewrite to a bucketed cache key or select a variant, keeping the shared parts cacheable. Alternatively, serve a common shell and personalize the small dynamic parts client-side or via edge-side includes. The goal is a handful of variants, not per-user caching.

Range requests (Range: bytes=...) let clients fetch parts of a large object (seeking in a video, resuming a download). CDNs support this by caching the object in segments/chunks and serving the requested byte ranges, fetching missing ranges from the origin (range-aware origin pull). This avoids caching or transferring the whole file when a client only needs part of it, improves seek performance, and enables adaptive streaming. The origin must support range requests and consistent ETags so cached chunks stay valid.