CDN (Content Delivery Network)

6 interview angles 6 min read source

CDN (Content Delivery Network)

A CDN is a global mesh of caching reverse proxies near end users. When a user requests an asset, they hit the nearest edge instead of your origin server. Faster (less latency), cheaper (less origin bandwidth), more resilient (origin can be down briefly without users noticing for cached content).

Examples: CloudFront, Cloudflare, Fastly, Akamai, Bunny.net.

What’s at a CDN edge

         Origin (your servers, one region)
                    ↑ cache miss / origin pull

[ POP - London ] [ POP - Frankfurt ] [ POP - Tokyo ] [ POP - SF ] ...
       ↑                ↑                  ↑              ↑
     User             User              User           User

POP = Point of Presence. Each POP holds cached copies of assets and serves nearby users with single-digit-millisecond latency.

What a CDN actually does

  1. Caches static assets (JS bundles, CSS, images, video, fonts).
  2. Terminates TLS at the edge — handshake completes near the user.
  3. Compresses and optimizes (gzip/brotli, image resizing, WebP conversion).
  4. DDoS absorption — edge soaks attack traffic before it hits origin.
  5. WAF (web application firewall) — blocks SQLi, XSS patterns, bots.
  6. Origin shield — secondary cache layer that funnels misses, reducing origin requests.
  7. Edge compute (Cloudflare Workers, Lambda@Edge, Fastly Compute) — run code at the POP.

Cache hit vs miss

  • HIT: edge has the asset, serves it immediately. ~1–10ms.
  • MISS: edge fetches from origin (or origin shield), caches, then serves. First request pays origin RTT; subsequent requests are HITs.

CDN status often surfaces in headers: X-Cache: HIT, CF-Cache-Status: HIT, Age: 142 (seconds since cached).

A “stampede” happens when an asset expires and 1000 simultaneous requests all miss the cache and hit origin. Mitigations:

  • Stale-while-revalidate — serve stale content while fetching fresh in background.
  • Origin shield — only one POP fetches from origin; others fetch from shield.
  • Request coalescing — CDN dedupes identical concurrent misses into one origin request.

Cache control — the contract

The origin tells the CDN (and browsers) how long to cache and what conditions apply, via response headers.

Header Means
Cache-Control: public, max-age=3600 cacheable by anyone for 1 hour
Cache-Control: private cacheable only by the user’s browser, not by a shared cache
Cache-Control: no-cache revalidate every time (use ETag)
Cache-Control: no-store never cache, anywhere
Cache-Control: s-maxage=86400 shared caches (CDN) cache for 24h, regardless of max-age
Cache-Control: stale-while-revalidate=60 serve stale up to 60s while fetching fresh
Vary: Accept-Encoding, Authorization cache key includes these request headers
ETag: "abc123" resource version; client revalidates with If-None-Match
Last-Modified: ... revalidate with If-Modified-Since

Vary: Accept-Encoding is essential — without it, a gzipped response can be served to a client that doesn’t support gzip (broken page).

Vary: Cookie is dangerous — almost every request has a unique cookie, so cache hit rate collapses to ~0.

What to cache, what not to

Cache Don’t cache
Static assets with content-hashed filenames (app.abc123.js) logged-in user pages, anything with PII
Public marketing pages API responses for write endpoints
Public API GETs that don’t change often Set-Cookie responses
Images / video session-specific responses (unless Vary: Cookie is acceptable)

For SPAs, the standard pattern: cache app.<hash>.js for a year (immutable; URL changes when content changes); never cache index.html.

Cache invalidation

The hard one. Two approaches:

Strategy How
Versioned URLs (preferred) rename the file when it changes (app.abc123.jsapp.def456.js). Old cached version irrelevant; new URL is a fresh fetch.
Manual purge call CDN API to invalidate /path/.... Slow (seconds-to-minutes propagation), expensive at scale, error-prone.

Versioned URLs are why every modern build tool (webpack, Vite) emits content-hashed filenames.

For dynamic API responses, use short TTLs and stale-while-revalidate rather than purges.

Origin shield

Two-layer caching: POPs fetch from a single regional shield POP, which fetches from origin. Cuts origin traffic dramatically when you have many POPs (every miss across 200 POPs becomes one origin request, not 200).

Configurable in CloudFront / Cloudflare. For high-traffic origins, basically always enable.

Signed URLs / signed cookies

For paid/private content (videos, downloads):

  • Generate a URL with a signed query string (HMAC of path + expiry + account).
  • CDN validates the signature at the edge — invalid → 403.
  • Lets users access the asset for, say, 1 hour without giving them a permanent URL.

CloudFront and Cloudflare both support this. The signing happens in your app:

# CloudFront example (simplified)
import boto3
from botocore.signers import CloudFrontSigner
signer = CloudFrontSigner(KEY_PAIR_ID, rsa_signer)
signed_url = signer.generate_presigned_url(url, date_less_than=expiry)

CDN as the only entry point

Best practice: lock down origin to only accept requests from the CDN’s IP ranges. Otherwise attackers find the origin’s real IP and bypass the CDN’s WAF/DDoS protection.

  • AWS: ALB security group ingress restricted to CloudFront prefix list.
  • Cloudflare: Argo Tunnel / Authenticated Origin Pulls.

Edge compute

Run code at the POP for things that need to be near the user but not in your origin:

Use Example
A/B routing flip 5% of traffic to a new variant
Auth check verify JWT before letting it reach origin
Geo redirect EU users → eu.example.com
Image resizing ?w=400 returns a resized variant
Personalization at the edge inject a user’s name into a cached page

Lambda@Edge / Cloudflare Workers / Fastly Compute. Trade-off: cold-start latency, smaller language/runtime support, harder to debug than your main app.

Common interview confusions

  • “A CDN is just a cache.” — it’s also TLS termination, DDoS absorption, WAF, edge compute, image optimization. Caching is one feature.
  • “Cloudflare and CloudFront are interchangeable.” — operationally similar, but Cloudflare is a network with built-in features (WAF, DDoS, free tier); CloudFront is AWS-tightly-integrated and pay-per-use.
  • Cache-Control: max-age=3600 means it’ll always be cached for an hour.”max-age is for browsers/private caches; s-maxage is what shared caches (CDN) honor. Without s-maxage, CDN may use max-age or its own default. Specify both.
  • “Purging the CDN cache propagates instantly.” — usually 30s–several minutes globally, sometimes longer. Use versioned URLs instead.

Interview angle

  • “What does a CDN do beyond caching?” — TLS termination, DDoS absorption, WAF, compression, image optimization, edge compute, signed URLs.
  • “How do you cache an asset that changes occasionally?” — content-hashed filename + long Cache-Control: max-age=31536000, immutable. When the asset changes, the filename changes, so caching is automatic.
  • max-age vs s-maxage?”max-age is for browsers/private caches; s-maxage overrides for shared caches (CDN). Specify s-maxage to control CDN TTL independently of browser TTL.
  • “What does Vary: Accept-Encoding do?” — tells the CDN to cache different versions per Accept-Encoding value. Without it, a gzipped cached response could go to a client that didn’t ask for gzip.
  • “Cache stampede — how do you mitigate?”stale-while-revalidate, request coalescing at the CDN, origin shield (single layer between POPs and origin).
  • “How do you serve user-specific content via a CDN?” — generally don’t (cache hit rate dies). Either use signed URLs (per-user authorization, but the bytes are still public-cacheable), or do edge personalization with edge compute, or cache the public shell and load user-specific bits via separate uncached API calls.