solidcodersolidcoder
AI TopicsExplore Courses
solidcodersolidcoder

Practical courses for software engineering interviews — no gatekeeping, no fluff.

High Level Design

  • High Level Design Concepts
  • PostgreSQL Internals
  • System Design Course

Explore

  • Courses
  • AI Engineering Topics
  • About
  • Privacy Policy
  • Terms

© 2026 solidcoder · Practical courses for software engineering interviews.

Built for the AI era — learn by doing.

Home/High-Level Design/System Design Course/CDN Explained: How It Works, Setup & Cache Invalidation
Chapters — System Design Course▾

CDN Explained: How It Works, Setup & Cache Invalidation

32 min read·Sep 25, 2026
Keywords:Content Delivery NetworkEdge CacheCache-ControlCache PurgeOrigin ShieldAnycaststale-while-revalidateSurrogate KeyCloudFrontCloudflareSystem Design InterviewHow to Invalidate CDN CacheCloudFront InvalidationPurge CDN CacheCDN Cache HeadersContent Delivery Network TutorialHow CDN Works

Learn how a CDN works, how to enable one, which cache headers matter, and every way to invalidate or purge a CDN cache, with diagrams and interview answers.

CDN Explained: How It Works, Setup & Cache Invalidation — System Design Course
Learn how a CDN works, how to enable one, which cache headers matter, and every way to invalidate or purge a CDN cache, with diagrams and interview answers.
TL;DR

A CDN (Content Delivery Network) keeps copies of your content on servers close to your users, so pages load faster and your own servers do far less work.

To use one: point your domain at it, send correct Cache-Control headers, and lock your origin down. To keep content fresh: prefer versioned file names and short TTLs, use tag or URL purges for targeted changes, and treat purge everything as a last resort.

This guide follows the story in order: the problem, the life of one request, what gets cached, how long, how to turn it on, how to keep it fresh, how to secure and scale it, and how to talk about it in a senior-level interview.

How to Read This Guide

Each part builds on the one before it. If you are short on time, read the TL;DR above and the cheat sheet in Part 11. If you are preparing for an interview, read everything, then jump to Part 10.

The journey
Part
The question it answers
1. The problem
Why does distance make websites slow, and what is a CDN?
2. Life of a request
What exactly happens, step by step, when a user loads a URL through a CDN?
3. What gets cached
How does the CDN decide two requests are the same, and what should I cache?
4. How long it stays
How do I control freshness with Cache-Control?
5. Turning it on
How do I actually enable a CDN for my site?
6. Keeping it fresh
How do I invalidate or purge content, including all of it?
7. Securing it
What can go wrong, and how do I protect users and the origin?
8. Scaling and observing
How do I make it fast, cheap and measurable?
9. The full picture
How does everything fit together in one real example?
10. Interview playbook
How do I answer CDN questions like a senior architect?

Part 1 — The Problem: Why Distance Hurts, and What a CDN Is

1.1 An intuition first

Imagine one giant warehouse in Virginia that holds every product your company sells. Every customer in the world orders from it. A customer in Bengaluru waits days; the warehouse is overwhelmed on sale day; and if it burns down, nobody gets anything.

Now imagine the same company opens hundreds of small local stores. Each store keeps the popular items in stock. Most orders are filled from the store down the road. Only rare items, or items the store has not stocked yet, are fetched from the warehouse.

That is a CDN.

The mental model to remember

CDN = a worldwide chain of local stores (edges) that stock copies of your popular content, backed by one central warehouse (your origin).

Everything else in this guide is about three questions: what should the stores stock, how long before it goes stale, and how do I tell them when something changed.

1.2 The vocabulary

Terms you will see in every CDN conversation
Term
Plain-English meaning
Origin
Your real server, or an object store like S3. The source of truth, the warehouse.
Edge / PoP (Point of Presence)
A CDN data center near users that stores copies of your content. The local store.
Cache hit
The edge already has a fresh copy and answers immediately.
Cache miss
The edge does not have it (or it is stale), so it fetches it from the origin first.
TTL (time to live)
How long a cached copy is considered fresh.
Origin shield / tiered cache
An optional middle layer: edges ask one regional cache before bothering the origin. Covered in Part 8.
Purge / invalidation
Telling the CDN to throw away cached copies before their TTL ends. Covered in Part 6.

1.3 What a CDN actually fixes

Our running example is a Twitter-like app. A single page load pulls an avatar, a JavaScript bundle, CSS, tweet images and a timeline API response.

Five problems, one solution
Problem
How a CDN helps
Latency
Light in fibre covers roughly 200 km per millisecond. A Bengaluru to Virginia round trip is 200+ ms before any server work, and TCP and TLS handshakes multiply it. An edge in the same city is about 5–20 ms away.
Origin load
A viral tweet's image requested 10 million times reaches your origin about once per edge (or once in total with a shield), not 10 million times.
Bandwidth cost and capacity
Bytes are served from the CDN's network, which has far more aggregate capacity than your servers and is usually cheaper per GB.
Availability
If your origin has a bad minute, edges can keep serving slightly stale copies (stale-if-error).
Security
The edge absorbs DDoS traffic, terminates TLS, and can run a WAF and bot protection before bad traffic reaches you.
Key takeaway

A CDN trades a little freshness for a lot of speed, scale and resilience. Every decision later in this guide is about controlling that trade.

Part 2 — The Life of a Request

Let us follow one request, in order, from the moment a user types a URL.

flowchart LR
  A[1. DNS lookup] --> B[2. Route to nearest edge]
  B --> C[3. TCP + TLS to the edge]
  C --> D[4. Cache lookup]
  D -->|hit| E[5a. Serve from edge]
  D -->|miss| F[5b. Fetch from origin, store, serve]

  style A fill:#6B7280,stroke:#4B5563,color:#fff
  style B fill:#8B5CF6,stroke:#6D28D9,color:#fff
  style C fill:#8B5CF6,stroke:#6D28D9,color:#fff
  style D fill:#06B6D4,stroke:#0E7490,color:#fff
  style E fill:#22C55E,stroke:#15803D,color:#fff
  style F fill:#3B82F6,stroke:#2563EB,color:#fff

The flowchart above is the whole story in five boxes. The rest of this part zooms into each one.

2.1 Step 1: DNS decides, not the URL

A common beginner question is: how does a URL know whether to hit the CDN or the origin?

It does not. DNS decides which server receives the request. When you enable a CDN, you change what your hostname resolves to:

Before:  www.example.com  →  A      203.0.113.10             (your origin server)
After:   www.example.com  →  CNAME  d123.cloudfront.net      (the CDN)
                              → resolves to a nearby edge IP

From that moment, every browser asking for www.example.com receives the CDN's address, so requests reach the CDN automatically. With Cloudflare's proxied mode (the orange cloud), the public DNS answer is Cloudflare's edge IP, and your real origin IP is never exposed.

The CDN then looks up your hostname in its own configuration to learn where your origin is:

Host: www.example.com  →  CDN config  →  origin = origin.example.com (or an S3 bucket)

The browser never sees the origin address. Only the CDN does.

2.2 Step 2: Finding the nearest healthy edge

Your CDN has hundreds of edges worldwide, but your users all type the same hostname. Something has to answer one question for each user: which edge should this person talk to?

There are two ways to answer it. A phone-line analogy makes the difference easy to remember.

The analogy

DNS-based routing is calling a company's main office, where a receptionist says: 'For you, please call the Chennai branch on this number.' Different callers get different numbers.

Anycast is a company with one national phone number. The phone network itself connects each caller to the nearest branch. Everyone dials the same number.

Short version: DNS decides which IP address you use. Anycast makes one IP address lead to the nearest machine.

Method A: DNS-based routing (GeoDNS, latency-based)

Every user gets a different IP address, chosen for them at the moment they look up the hostname.

sequenceDiagram
  autonumber
  actor U as User (Bengaluru)
  participant R as User's DNS Resolver
  participant A as CDN Authoritative DNS
  participant M as CDN Mapping System

  U->>R: Where is www.example.com?
  R->>A: Query (+ optional client subnet hint)
  A->>M: Which PoP is best for this location?
  Note over M: Inputs: network location, measured latency,<br/>PoP health, PoP load, cost, customer rules
  M-->>A: Chennai PoP (203.0.113.7)
  A-->>R: 203.0.113.7 (TTL 30s)
  R-->>U: 203.0.113.7
  Note over U: Different users get different IPs

The CDN runs its own authoritative DNS server and a mapping system behind it. The mapping system continuously measures latency between networks and PoPs, and tracks which PoPs are healthy and how busy they are. The DNS answer is the result of that calculation.

DNS-based routing: strengths and weaknesses
Aspect
What to know
Strength: fine-grained control
The CDN can steer traffic on purpose: away from an overloaded PoP, towards a cheaper region, or according to per-customer rules. It can shift 10% of traffic, not just all of it.
Strength: sees health and load
Because the answer is computed fresh, a sick or full PoP can simply stop being returned.
Weakness: it sees the resolver, not the user
The CDN only sees who asked it, which is the user's DNS resolver. A user in Bengaluru using a resolver in Singapore may be mapped to Singapore. EDNS Client Subnet (ECS) lets resolvers pass part of the user's network prefix to fix this, but not every resolver sends it, partly for privacy reasons.
Weakness: caching slows failover
Answers are cached by resolvers, operating systems and browsers for the TTL (often tens of seconds). If a PoP dies, some users keep going to it until their cached answer expires, and a few resolvers ignore low TTLs.
Weakness: nearest on a map is not always fastest
Network paths do not follow geography. Good providers rely on measured latency and real-user data, not just a user's coordinates.

Method B: Anycast

Every user gets the same IP address. Many PoPs around the world announce that address to the internet using BGP, the protocol networks use to tell each other which IP ranges they can reach.

flowchart TB
  U1[User in Bengaluru] --> I1[ISP router]
  U2[User in Frankfurt] --> I2[ISP router]
  U3[User in Dallas] --> I3[ISP router]
  I1 -->|shortest BGP path| P1[PoP Chennai<br/>announces 198.51.100.10]
  I2 -->|shortest BGP path| P2[PoP Frankfurt<br/>announces 198.51.100.10]
  I3 -->|shortest BGP path| P3[PoP Dallas<br/>announces 198.51.100.10]

  style U1 fill:#6B7280,stroke:#4B5563,color:#fff
  style U2 fill:#6B7280,stroke:#4B5563,color:#fff
  style U3 fill:#6B7280,stroke:#4B5563,color:#fff
  style I1 fill:#3B82F6,stroke:#2563EB,color:#fff
  style I2 fill:#3B82F6,stroke:#2563EB,color:#fff
  style I3 fill:#3B82F6,stroke:#2563EB,color:#fff
  style P1 fill:#8B5CF6,stroke:#6D28D9,color:#fff
  style P2 fill:#8B5CF6,stroke:#6D28D9,color:#fff
  style P3 fill:#8B5CF6,stroke:#6D28D9,color:#fff

The diagram shows three users sending packets to the same IP. Each one's own ISP forwards the packets along the best BGP path it knows, which usually ends at the nearest PoP. Nobody made a per-user decision. The internet's routing did it.

Failover is the party trick. If a PoP fails, it stops announcing the address (or its BGP session drops). Within seconds to a minute, routers around the world learn that path is gone, and traffic flows to the next-nearest PoP. No DNS answers need to expire.

Anycast: strengths and weaknesses
Aspect
What to know
Strength: fast failover
Withdrawing a route redirects traffic without waiting on DNS caches.
Strength: natural DDoS absorption
Attack traffic enters the network wherever the attackers are, so it is spread across many PoPs instead of piling onto one.
Strength: simple for clients
One stable IP, no dependence on how smart the user's DNS resolver is.
Weakness: BGP picks 'best path', not 'lowest latency'
Routers choose based on network topology and policy, usually fewest hops between networks. That is often, but not always, the closest PoP, so some users take a detour.
Weakness: coarse load control
It is harder to say 'send exactly 20% elsewhere'. Operators use route tweaks such as AS-path prepending and BGP communities, which are blunter than DNS.
Weakness: route changes can disturb connections
If routing shifts mid-connection, packets may land on a different PoP that knows nothing about that TCP session. This is rare, and short-lived requests, QUIC, and careful engineering reduce the impact.

Side by side

DNS-based routing vs Anycast
Question
DNS-based | Anycast
Who decides?
The CDN's DNS and mapping system | Internet routers, via BGP
What does the user get?
A different IP per location | The same IP everywhere
Decision based on
Measured latency, health, load, cost | Network topology and routing policy
Control over traffic split
Fine | Coarse
Failover speed
Limited by DNS TTL and caching | Seconds to about a minute (BGP convergence)
DDoS behaviour
Attack can follow the DNS answer to one PoP | Naturally spread across PoPs
Main weakness
Resolver location is only a proxy for the user | 'Best BGP path' is not always the lowest latency

In practice: most CDNs combine them

The two are not rivals. They solve different layers of the problem, and real providers mix them:

  • DNS returns anycast addresses. Even when DNS does the first step, the IPs it returns may be anycast, so each region still has fast failover underneath.
  • DNS picks a region or cluster, anycast picks the machine. Coarse steering from DNS, with routing and load balancing inside the PoP doing the rest.
  • Different products, different mixes. Cloudflare and Fastly lean heavily on anycast. Akamai and CloudFront have historically relied heavily on DNS-based mapping, and also offer anycast options in places. Architectures change, so check each provider's current documentation.
What this means for you as a customer

You rarely configure any of this. You just point a CNAME at the CDN. But it affects three practical things:

  1. Failover design. DNS-based steering means DNS TTLs matter. Anycast means failover is faster but not instant.
  2. Debugging. To see which PoP served you, read the response headers: CloudFront shows x-amz-cf-pop (for example a code starting with BLR), and Cloudflare's cf-ray ends with the data center code.
  3. Interviews. You are expected to explain both mechanisms, plus their trade-offs.
One-line interview answer

'DNS-based routing computes a nearest-PoP IP per user using measured latency, health and load, which gives fine control but is limited by DNS caching. Anycast announces one IP from many PoPs and lets BGP deliver users to a nearby one, which fails over fast but gives coarser control. Most CDNs combine both.'

2.3 Steps 3 to 5: connect, look up, answer

Once the browser has the edge's IP, it connects (TCP and TLS, over a short distance), then asks for the object. The edge looks it up in its cache.

sequenceDiagram
  autonumber
  actor U as User (Bengaluru)
  participant R as Recursive DNS Resolver
  participant A as CDN Authoritative DNS
  participant E as Edge PoP (Chennai)
  participant O as Origin (us-east-1)

  U->>R: Resolve www.example.com
  R->>A: Query (CNAME to d123.cdn-provider.net)
  A->>A: Pick best PoP (geo, latency, health, load)
  A-->>R: A record = Chennai PoP IP (short TTL)
  R-->>U: Chennai PoP IP
  U->>E: TCP + TLS handshake (short round trip)
  U->>E: GET /static/app.9f3c1a.js
  alt Cache hit
    E-->>U: 200 from edge memory or disk
  else Cache miss
    E->>O: GET (over a warm, reused connection)
    O-->>E: 200 + Cache-Control: max-age=31536000
    E->>E: Store object
    E-->>U: 200
  end

The diagram shows DNS steering a user in Bengaluru to a Chennai PoP, followed by either a hit or a miss.

2.4 Hit vs miss, side by side

The first request for an object is a miss: the edge fetches it, stores it, and answers. Every later request is a hit: the origin is never involved.

sequenceDiagram
  autonumber
  actor U1 as User A (Mumbai)
  actor U2 as User B (Mumbai)
  participant E as Edge PoP (Mumbai)
  participant O as Origin (us-east-1)

  Note over U1,O: First request: cache MISS
  U1->>E: GET /img/avatar_42.jpg
  E->>E: Lookup cache key, not found
  E->>O: GET /img/avatar_42.jpg
  O-->>E: 200 + Cache-Control: max-age=86400
  E->>E: Store object (TTL 24h)
  E-->>U1: 200 (X-Cache: Miss, Age: 0)

  Note over U1,O: Second request: cache HIT
  U2->>E: GET /img/avatar_42.jpg
  E->>E: Lookup, found, fresh
  E-->>U2: 200 (X-Cache: Hit, Age: 312)

User B never touches the origin. The cost of the miss is paid once per object per edge, or even less with a shield.

2.5 Two common setups

How teams wire hostnames
Setup
How it works
Separate hostnames
static.example.com goes to the CDN, api.example.com goes straight to your servers. DNS alone decides.
One hostname through the CDN (most common today)
www.example.com always reaches the CDN, and path rules decide per request: /static/* is cached for a year, /api/public/* for 30 seconds, and /api/home or POST /tweets passes straight through to the origin.

Two details trip people up:

  • The origin needs its own name. If the origin were also called www.example.com, the CDN would loop back to itself. Use something like origin.example.com, or an IP address, with the original Host header forwarded.
  • Someone can skip the CDN if they learn your origin's address. That bypasses your cache, WAF and rate limits. Part 5.5 shows how to lock the origin down.
A subtle win people miss

Even for uncacheable requests (an API call marked no-store), a CDN helps. The slow TCP and TLS handshakes happen over a short hop to the edge, and the edge reuses long-lived, pre-warmed connections to the origin. This is called dynamic site acceleration.

Key takeaway

The URL never chooses. DNS sends users to the CDN, and the CDN then chooses hit, miss or pass-through based on its cache key and rules. That is the subject of the next part.

Part 3 — Deciding What Gets Cached

Before the edge can say 'I have this', it has to know what 'this' means.

3.1 The cache key

The cache key is the CDN's definition of 'the same object'. By default it is roughly scheme + host + path + query string. Two requests with the same key share one cached copy, and two requests with different keys do not.

You control the key with a cache policy. Choosing it well is the single biggest factor in your hit ratio.

Cache key design
Component
Guidance
Query string
Include only parameters that change the response (like ?w=400). Ignore tracking parameters such as utm_source, or you create thousands of duplicate objects and wreck your hit ratio.
Headers
Add only what truly matters, e.g. Accept-Encoding (normalised to gzip or br), or Accept-Language if you localise. Every extra header multiplies cache fragmentation.
Cookies
Exclude by default. One session cookie in the key gives every user a private cache entry, which is the same as having no cache.
Vary header from the origin
Vary: Accept-Encoding is fine. Vary: Cookie or Vary: User-Agent is a hit-ratio killer, so normalise these at the edge.
The classic outage

A response that differs per user (a logged-in page) but is cached under a shared key will be served to other users. That is a data leak, not a performance bug. When in doubt, use Cache-Control: private or no-store, and never let the CDN cache a response that carries Set-Cookie.

3.2 What to cache and what never to

Classify your content first. Each class gets its own strategy.

Twitter-like system: content classification
Content
CDN strategy
JS, CSS, fonts with hashed names (app.9f3c1a.js)
Cache for a year and mark immutable. These never need invalidation, because a new deploy produces a new file name.
Tweet images, videos, avatars
Cache long (days to a year). Use versioned or content-addressed URLs so an avatar change creates a new URL.
index.html (the app shell)
Short TTL, or no-cache with revalidation, because it points at the latest hashed assets.
Public API, e.g. GET /tweets/101 or trending
Cache for seconds to minutes. Even 1–5 seconds collapses a huge read stampede.
Personalised timeline GET /home
Usually private and no-store. Cache per user only with a carefully built key, or cache non-personalised fragments and compose at the edge or in the client.
Writes (POST /tweets), auth, payments
Never cached. Pass through to the origin.
Key takeaway

Decide what counts as the same object (the key) and which content is safe to share (the classification). Next you decide how long each class may be reused.

Part 4 — Controlling Freshness with Cache-Control

The origin tells every cache what to do, using HTTP response headers. Learning these is the single most valuable CDN skill.

4.1 The directives that matter

Think of them in three groups: how long, who may store it, and what to do when it expires or fails.

Group 1: how long is it fresh?
Directive
Meaning
max-age=N
Fresh for N seconds in any cache, browsers and CDNs alike.
s-maxage=N
Same, but for shared caches (the CDN) only, and it overrides max-age there. This lets you cache 5 minutes at the edge while the browser caches 0.
immutable
The content will not change while it is fresh, so browsers should not even revalidate on reload. Pair it with hashed file names.
Group 2: who may store it?
Directive
Meaning
public
Shared caches may store it, even for requests that carry an Authorization header.
private
Only the end user's browser may store it. The CDN must not.
no-store
Do not store it anywhere, ever.
no-cache
A misleading name. It may be stored, but it must be revalidated with the origin before each reuse.
Group 3: what happens at expiry or on errors?
Directive
Meaning
stale-while-revalidate=N
After expiry, serve the stale copy instantly for up to N more seconds while refreshing in the background. Users never wait on the origin.
stale-if-error=N
If the origin errors or times out, keep serving the stale copy for up to N seconds. A cheap availability boost.

These directives are defined in RFC 9111 (HTTP Caching) and RFC 5861 (stale-while-revalidate and stale-if-error). MDN's Cache-Control reference is a handy companion.

4.2 Defaults for our system

# Hashed static assets
Cache-Control: public, max-age=31536000, immutable

# App shell (index.html)
Cache-Control: no-cache

# Public API (edge 30s, browser 0, serve stale while refreshing)
Cache-Control: public, max-age=0, s-maxage=30, stale-while-revalidate=60, stale-if-error=300

# Personalised response
Cache-Control: private, no-store

4.3 Revalidation: cheap refreshes with ETag

When an object expires, the edge does not have to download it again. It asks 'has this changed?' using a validator such as an ETag. If the origin answers 304 Not Modified, only headers travel, and the edge resets the freshness timer.

sequenceDiagram
  autonumber
  actor U as User
  participant E as Edge
  participant O as Origin

  U->>E: GET /api/trending
  Note over E: Cached copy is stale (TTL expired), ETag "v42"
  E->>O: GET /api/trending (If-None-Match: "v42")
  alt Unchanged
    O-->>E: 304 Not Modified (no body, tiny)
    E->>E: Reset freshness timer
  else Changed
    O-->>E: 200 + new body + ETag "v43"
    E->>E: Replace cached object
  end
  E-->>U: 200 (fresh content)

4.4 stale-while-revalidate: nobody waits

With stale-while-revalidate, an expired object is served instantly while the edge refreshes it in the background.

sequenceDiagram
  autonumber
  actor U as User
  participant E as Edge
  participant O as Origin

  Note over E: s-maxage=30, stale-while-revalidate=60
  U->>E: GET /api/trending at t=45s (expired, inside SWR window)
  E-->>U: 200 stale copy, instantly
  E->>O: Background revalidate (async)
  O-->>E: 200 new content
  E->>E: Update cache
  U->>E: GET /api/trending at t=50s
  E-->>U: 200 fresh copy
Key takeaway

The origin is the boss. It sets the rules in headers, and the CDN follows them. A good default is: hashed assets for a year, HTML revalidated, public APIs for seconds with stale-while-revalidate, private data never shared.

Part 5 — Turning It On: How to Enable a CDN

You now know how a request flows, what gets cached, and how long. Time to switch it on.

5.1 Pull or push

Two delivery models
Model
How it works
Pull (origin pull), the default in almost every case
You declare an origin. The edge fetches objects lazily on the first miss. No upload step. Content appears and expires automatically.
Push
You upload content into the CDN's storage yourself and delete it yourself. Useful for large, rarely changing libraries such as software downloads or video archives.

5.2 The enablement checklist

Follow these in order. Each step protects the next.

Step by step (pull CDN, any provider)
Step
What to do
1. Split static from dynamic
Serve assets from a separate hostname (static.example.com) or a path prefix (/static/*, /media/*), so you can apply aggressive caching without endangering dynamic pages.
2. Fix your origin headers
Make the origin emit Cache-Control, ETag or Last-Modified, and compression. The CDN is only as good as the instructions you give it (Part 4).
3. Create a distribution or service
Register the origin (domain or bucket), force HTTPS, and set origin timeouts.
4. Define rules per path pattern
For example /static/* cached for a year, /api/public/* for 30 seconds, and /* passed through. Attach a cache policy (what is in the key) and an origin request policy (what is forwarded to the origin).
5. Add your domain and TLS certificate
Request a certificate (for CloudFront it must be issued in us-east-1; Cloudflare issues one automatically) and add your domain name to the distribution.
6. Lock down the origin
Make the origin reachable only through the CDN, so attackers cannot bypass your cache, WAF and rate limits (see 5.5).
7. Lower the DNS TTL, then point DNS at the CDN
CNAME the hostname to the distribution (use ALIAS or ANAME at an apex domain, or let the CDN host your DNS). A low TTL beforehand makes rollback fast.
8. Verify
Use curl -I and read the cache headers (see 5.6), then watch the hit ratio in the dashboard.
9. Roll out gradually
Start with static assets, then public API routes. Use weighted DNS or a feature flag to shift traffic, and watch error rate and origin load.

5.3 Example A: AWS CloudFront in front of S3

resource "aws_cloudfront_origin_access_control" "oac" {
  name                              = "tweets-media-oac"
  origin_access_control_origin_type = "s3"
  signing_behavior                  = "always"
  signing_protocol                  = "sigv4"
}

resource "aws_cloudfront_distribution" "media" {
  enabled      = true
  aliases      = ["media.example.com"]
  http_version = "http2and3"
  price_class  = "PriceClass_All"

  origin {
    domain_name              = aws_s3_bucket.media.bucket_regional_domain_name
    origin_id                = "s3-media"
    origin_access_control_id = aws_cloudfront_origin_access_control.oac.id
  }

  default_cache_behavior {
    target_origin_id       = "s3-media"
    viewer_protocol_policy = "redirect-to-https"
    allowed_methods        = ["GET", "HEAD", "OPTIONS"]
    cached_methods         = ["GET", "HEAD"]
    compress               = true
    # Managed policy: CachingOptimized
    cache_policy_id        = "658327ea-f89d-4fab-a63d-7e88639e58f6"
  }

  viewer_certificate {
    acm_certificate_arn = aws_acm_certificate.media.arn # must be in us-east-1
    ssl_support_method  = "sni-only"
  }

  restrictions { geo_restriction { restriction_type = "none" } }
}

The bucket policy then allows s3:GetObject only to the CloudFront service principal for this distribution. This is Origin Access Control (OAC), so the bucket is never public.

5.4 Example B: Cloudflare (proxy mode)

  1. Add the site, and change your registrar's nameservers to Cloudflare's.
  2. In DNS, set the record for www (or the apex) to Proxied (the orange cloud). Traffic now flows through the edge.
  3. Cloudflare caches common static file types by default, but not HTML or JSON. Use Cache Rules to cache the paths you choose, set Edge TTL, and respect or override origin headers.
  4. Enable Tiered Cache, Brotli, HTTP/3 and 'Always Use HTTPS'.

5.5 Locking down the origin

Stop clients from bypassing the CDN
Technique
How
Private object store
S3 with OAC (or OAI), GCS signed access, Azure private endpoints. The bucket is never public.
Secret header
The CDN adds X-Origin-Verify: <random secret> to every origin request, and the origin or load balancer rejects requests without it. Rotate it periodically.
IP allowlisting
Allow only the CDN's published IP ranges at the firewall or security group, and keep the list updated.
mTLS / authenticated origin pulls
The CDN presents a client certificate and the origin only accepts connections that carry it. The strongest option.

5.6 Verify it works

curl -sI https://static.example.com/static/app.9f3c1a.js
HTTP/2 200
cache-control: public, max-age=31536000, immutable
content-encoding: br
age: 4821                    # seconds the object has lived at the edge
x-cache: Hit from cloudfront # CloudFront; Cloudflare uses cf-cache-status: HIT
via: 1.1 abc.cloudfront.net (CloudFront)
How to read the signals
Signal
Meaning
Age: 0 or X-Cache: Miss
The edge fetched this from the origin for this request.
Age: N (growing)
A cached copy that is N seconds old.
Always a MISS on repeat requests
Something prevents caching: Set-Cookie, Cache-Control private or no-store, Vary: *, a unique query string, or no matching rule.
cf-cache-status: DYNAMIC (Cloudflare)
The path is not eligible for caching under the current rules.
REVALIDATED or EXPIRED
The object had expired and the edge checked the origin, usually receiving a 304.
Key takeaway

Enabling a CDN is mostly DNS plus headers plus lockdown. Test with curl -I, and roll out one path class at a time.

Part 6 — Keeping Content Fresh: Cache Invalidation

Everything is cached and fast. Then you ship a fix, or delete a tweet, and the edges still serve the old version. This is the hard part.

'There are only two hard things in Computer Science: cache invalidation and naming things.'

It is hard because you are updating copies on hundreds of servers you do not control, while more copies sit in users' browsers.

6.1 Mental model: there are three layers of cache

flowchart LR
  B[Browser cache] --> E[Edge cache]
  E --> S[Shield cache]
  S --> O[(Origin)]

  style B fill:#6B7280,stroke:#4B5563,color:#fff
  style E fill:#8B5CF6,stroke:#6D28D9,color:#fff
  style S fill:#06B6D4,stroke:#0E7490,color:#fff
  style O fill:#3B82F6,stroke:#2563EB,color:#fff

A purge only clears the edge and shield layers. It does not reach the browser cache. If you served a file with max-age=1 year, every browser that has it keeps it, and you cannot recall it.

The rule that follows

Design so that browsers never hold content you might need to change. Hashed file names for assets, and no-cache (revalidate) for HTML.

6.2 The ladder of preference

Invalidation is a fallback, not a strategy. The best invalidation is none. Try these in order.

From best to last resort
Approach
When to use it
1. Versioned or content-hashed URLs
Static assets, media, uploads. Change the content, change the URL (app.9f3c1a.js, avatar_42_v7.jpg). The old URL stays cached harmlessly, and the new one is a fresh miss. Instant, free, and it also fixes browser caches.
2. Short TTL + stale-while-revalidate
Semi-dynamic public data such as trending lists, counters and profile JSON. Staleness is bounded by design, and no purge machinery is needed.
3. Targeted purge (URL or tag)
When specific content must change now: a deleted tweet, a legal takedown, a wrongly published page.
4. Purge everything
Break-glass only: a bad deploy, a poisoned cache, a mass configuration change. Plan for the consequences (see 6.7).

6.3 The full menu of techniques

Techniques compared
Technique
How it works and trade-offs
Wait for TTL expiry
Do nothing. Zero cost, but staleness equals the TTL. Fine when you chose the TTL knowingly.
Cache busting (versioned URL)
Change the URL (hash, version, or ?v=). Do not rely on query-string busting if your cache key ignores the query string.
Purge by exact URL
Remove one object from all edges. Precise and fast, but you must purge every variant (encodings, query strings, hostnames) that is in the key.
Purge by prefix, wildcard or path
For example /static/* or /u/42/*. One request covers many objects. Provider limits apply, and a very broad wildcard behaves like a mini purge-all.
Purge by tag (surrogate key or cache tag)
The origin labels responses with tags (Surrogate-Key: tweet-101 user-42 timeline). Purge a tag and every object carrying it disappears, however many URLs it spans. The best model for dynamic content. Fastly (Surrogate-Key), Cloudflare (Cache-Tag) and Akamai (Edge-Cache-Tag) support it; CloudFront has no native tag purge, so you keep your own tag-to-path mapping.
Soft purge (mark as stale)
The object is marked stale instead of deleted. With stale-while-revalidate the edge serves it once more while fetching a fresh copy, so nobody waits and the origin is not stampeded. Fastly supports this natively.
Purge everything
Clears the whole cache for the service or zone. Instant to request, dangerous to run (see 6.7).
Cache-key change
Change the cache policy (for example add a version value to the key). Every old entry is simply never matched again, which invalidates everything for that rule without a purge API call.
TTL override or dev mode
Temporarily set edge TTL to 0 or enable development mode while debugging.

6.4 Commands: invalidating all of it

# --- AWS CloudFront ---
# '/*' counts as ONE invalidation path. The first 1,000 paths per month are free,
# after that you are charged per path.
aws cloudfront create-invalidation \
  --distribution-id E123ABC456DEF \
  --paths "/*"

aws cloudfront create-invalidation --distribution-id E123ABC456DEF \
  --paths "/static/*" "/index.html"

# --- Cloudflare ---
curl -X POST "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/purge_cache" \
  -H "Authorization: Bearer $CF_TOKEN" -H "Content-Type: application/json" \
  --data '{"purge_everything": true}'

# by URL / by tag / by prefix:
#   {"files": ["https://example.com/a.css"]}
#   {"tags": ["tweet-101"]}
#   {"prefixes": ["example.com/static/"]}

# --- Fastly ---
curl -X POST "https://api.fastly.com/service/$SERVICE_ID/purge_all" \
  -H "Fastly-Key: $FASTLY_TOKEN"

# purge by surrogate key (soft purge: add header Fastly-Soft-Purge: 1)
curl -X POST "https://api.fastly.com/service/$SERVICE_ID/purge/tweet-101" \
  -H "Fastly-Key: $FASTLY_TOKEN"
Verify limits and pricing on the day

Rate limits, per-path charges, propagation time and available purge types change by provider and plan. In an interview, state the pattern and say you would check current limits. In production, read the docs.

6.5 Pattern 1: the clean deploy (zero purge for assets)

Upload new hashed files first, upload the HTML last, and let old and new versions coexist.

sequenceDiagram
  autonumber
  participant CI as CI/CD
  participant S3 as Origin Bucket
  participant CDN as CDN
  actor U as Users

  CI->>CI: Build, filenames hashed (app.9f3c1a.js)
  CI->>S3: Upload new hashed assets FIRST
  Note over S3: Old and new assets coexist
  CI->>S3: Upload index.html LAST (Cache-Control: no-cache)
  CI->>CDN: Purge only /index.html (or nothing, if revalidating)
  U->>CDN: GET /index.html
  CDN-->>U: New HTML referencing app.9f3c1a.js
  U->>CDN: GET /static/app.9f3c1a.js
  CDN-->>U: Miss, then cached for 1 year
  Note over U,CDN: Users on the old page still fetch the old hashed file successfully

Uploading assets before the HTML, and keeping old assets around for a while, prevents 404s for users who are mid-session on the previous version.

6.6 Pattern 2: tag-based purge for dynamic content

The origin labels each response with surrogate keys. When a tweet is deleted, an event-driven worker purges that tag after the database commit, so the next read misses and gets a fresh 404.

sequenceDiagram
  autonumber
  actor Author as Alice
  participant API as Tweet Service
  participant DB as Database
  participant Q as Event Bus
  participant P as Purge Worker
  participant CDN as CDN
  actor Reader as Bob

  Reader->>CDN: GET /api/tweets/101
  CDN->>API: Miss
  API-->>CDN: 200 + Surrogate-Key: tweet-101 user-42 + s-maxage=300
  CDN-->>Reader: 200 (cached for 5 min)

  Author->>API: DELETE /tweets/101
  API->>DB: BEGIN, delete, COMMIT
  API->>Q: Publish TweetDeleted(101)
  Q->>P: Consume event
  P->>CDN: Purge tag tweet-101 (soft or hard)
  CDN-->>P: Accepted
  Reader->>CDN: GET /api/tweets/101
  CDN->>API: Miss (purged)
  API-->>CDN: 404
  CDN-->>Reader: 404

Note the order: commit to the database first, then purge. Purging before the commit lets the edge re-cache the old data. Driving purges from an event (or a transactional outbox) also makes them retryable if the CDN API call fails.

6.7 What happens when you purge everything

A purge is an API call to the CDN control plane, which fans the command out to every PoP. Propagation typically takes from under a second to a few minutes, depending on the provider.

The danger is what comes next: every edge is suddenly empty, and traffic that was being absorbed by caches now falls on your origin all at once. This is the thundering herd, or cache stampede.

sequenceDiagram
  autonumber
  actor Admin as Deploy pipeline
  participant CP as CDN Control Plane
  participant E1 as Edge PoP 1
  participant E2 as Edge PoP N
  participant Sh as Origin Shield
  participant O as Origin
  actor Users

  Admin->>CP: POST purge (/* or tag=tweet-101)
  CP-->>Admin: 201 accepted (async)
  par Fan out
    CP->>E1: Invalidate matching keys
  and
    CP->>E2: Invalidate matching keys
  end
  Note over E1,E2: Cache now empty (or marked stale)
  Users->>E1: 100,000 req/s for hot object
  Users->>E2: 100,000 req/s for hot object
  E1->>Sh: Miss (collapsed into 1 request)
  E2->>Sh: Miss (collapsed into 1 request)
  Sh->>O: 1 request (collapsed)
  O-->>Sh: 200
  Sh-->>E1: 200 (cached)
  Sh-->>E2: 200 (cached)
  E1-->>Users: Served
  E2-->>Users: Served

The diagram shows how an origin shield with request collapsing turns a storm of misses into a single origin request.

What goes wrong with purge-all, and the defence
Risk
Mitigation
Stampede: everything misses and the origin is flooded
Enable origin shield or tiered cache, and request collapsing (concurrent misses for one key become one origin request). Prefer soft purge. Purge in stages (static first, then API) or per region. Make sure origin autoscaling and rate limits are ready.
Browser caches are untouched
A CDN purge does not clear users' browsers. Anything served with a long max-age stays stale there until it expires. Hence hashed URLs and no-cache HTML.
Purge is asynchronous
Do not purge and immediately assume it is done. Poll, or verify Age and X-Cache from several regions.
Race: stale object re-cached after the purge
A slow origin response carrying old data arrives after the purge and gets cached. Purge after the write is committed, and for critical content consider purging twice (immediately and a few seconds later).
Cost and rate limits
Invalidation APIs are rate limited and sometimes billed per path. Use tags or wildcards instead of purging thousands of URLs one by one.
Key takeaway

Version the URL if you can, shorten the TTL if you cannot, tag-purge if you must, purge-all only in an emergency. And remember that purges never reach browsers.

Part 7 — Securing the CDN

Caching adds a new risk: shared storage of responses. These are the failure modes a senior engineer checks.

Threats and defences
Topic
What to check
Cache poisoning
An attacker sends a request with an unkeyed header (such as X-Forwarded-Host) that changes the response, and the poisoned response is cached for everyone. Defence: only reflect inputs that are in the cache key, strip or ignore unknown headers, validate Host.
Cache deception
Tricking the cache into storing a private page, e.g. by requesting /account/settings/fake.css. Defence: decide cacheability from the origin's Cache-Control, not file extension alone, and set no-store on private routes.
Private or paid content
Use signed URLs or signed cookies with an expiry (CloudFront, Cloudflare, Fastly token auth). The CDN validates the signature at the edge, so unauthorised users never reach your origin.
DDoS and abuse
The CDN absorbs volumetric attacks. Add WAF rules, rate limiting and bot management at the edge, hide origin IPs, and enforce origin lockdown (Part 5.5).
TLS
Terminate at the edge with modern TLS (1.2 and 1.3), enable HSTS, and use HTTPS between edge and origin. 'Flexible SSL' (plain HTTP to the origin) is a security smell.
CORS
If you serve fonts or API JSON cross-origin, set Access-Control-Allow-Origin correctly and send Vary: Origin so one origin's CORS response is not served to another.
Compliance and data residency
Cached copies exist in many countries. For regulated data, restrict PoP regions, use geo-fencing, or do not cache it.
Takedowns
Keep a runbook that purges by URL or tag within minutes, and also deletes from the origin and any backup caches.
Key takeaway

Most CDN security incidents are one of two mistakes: a private response cached under a shared key, or an origin reachable without the CDN. Fix those two first.

Part 8 — Making It Fast, Cheap and Measurable

8.1 Origin shield and tiered caching

Without a shield, 200 PoPs can each miss on the same object, producing 200 origin requests. With a shield (a designated mid-tier cache near the origin), edges ask the shield first, and the shield collapses their misses into a single origin request.

flowchart LR
  U[Users worldwide] --> E1[Edge PoP A]
  U --> E2[Edge PoP B]
  U --> E3[Edge PoP C]
  E1 -->|miss| S[(Regional Shield Cache)]
  E2 -->|miss| S
  E3 -->|miss| S
  S -->|miss, collapsed| O[(Origin)]

  style U fill:#6B7280,stroke:#4B5563,color:#fff
  style E1 fill:#8B5CF6,stroke:#6D28D9,color:#fff
  style E2 fill:#8B5CF6,stroke:#6D28D9,color:#fff
  style E3 fill:#8B5CF6,stroke:#6D28D9,color:#fff
  style S fill:#06B6D4,stroke:#0E7490,color:#fff
  style O fill:#3B82F6,stroke:#2563EB,color:#fff

8.2 The levers

What moves the numbers
Lever
Effect
Cache hit ratio (CHR)
The headline metric. Track both request hit ratio and byte hit ratio. Aim for 95%+ on static content, and 80%+ blended is common. A low CHR is almost always a cache-key or header problem.
Request collapsing
Concurrent misses for one key wait on a single origin fetch, protecting the origin from hot-key bursts.
Negative caching
Cache 404s and 5xx for a few seconds so a missing or broken object does not hammer the origin on every request.
Compression
Brotli for text assets (smaller than gzip), pre-compressed at build time for static files. Never compress already-compressed media (jpg, mp4).
HTTP/2 and HTTP/3 (QUIC)
Multiplexing, header compression, and faster connection setup on lossy mobile networks.
Image optimisation at the edge
Resize and convert (WebP, AVIF) based on ?w= and the Accept header, then cache each variant. Normalise Accept into a few buckets to avoid fragmentation.
Range requests and video
Video is delivered as small segments (HLS or DASH, 2–6 s), each a normal cacheable object. For progressive MP4, the CDN caches byte ranges or slices.
Cache warming
Before a known spike (a live event, a launch), pre-request hot objects so the first wave of users hits warm caches.
Edge compute
Workers, Lambda@Edge, Compute@Edge: A/B tests, auth checks, redirects, header normalisation and personalisation near the user, without an origin trip.
Multi-CDN
Two providers behind a DNS or traffic-steering layer, for resilience and cost leverage. Costs: duplicate configuration, two sets of cache behaviour, and cold caches on failover.

8.3 Observability: what to watch

Metrics worth a dashboard
Metric
Why
Hit ratio by path and status
Reveals which route silently became uncacheable after a deploy.
Origin request rate and 5xx
Capacity and protection health. A sudden rise after a purge means a stampede.
TTFB and latency percentiles (p50, p95, p99) by region
Real user experience, and a way to spot a sick PoP.
Egress by origin vs edge
Cost tracking. CDN egress is typically the biggest line item.
Purge latency and failures
Confirms invalidation actually propagates, and alerts when a purge job is failing.
RUM and synthetic checks from several regions
Provider dashboards show edge health, not what the user's browser experienced.
Key takeaway

Once the basics work, the biggest wins come from hit ratio (clean cache keys), shielding (protect the origin), and measuring (so regressions are caught the day they ship).

Part 9 — The Full Picture: One Page Load in Our Twitter-Like App

Every concept so far, in a single flow. Bob in Bengaluru opens the app.

The browser resolves DNS to the nearest edge, revalidates the HTML, gets static assets and trending data from cache, fetches an image through the shield on a miss, and sends the personalised timeline straight to the origin without caching it.

sequenceDiagram
  autonumber
  actor U as Bob (Bengaluru)
  participant DNS as DNS / Anycast
  participant E as Edge PoP
  participant Sh as Shield
  participant API as App / API
  participant S3 as Media Store
  participant DB as DB + Read Replica

  U->>DNS: Resolve www.example.com
  DNS-->>U: Nearest edge
  U->>E: GET / (index.html, no-cache)
  E->>API: Revalidate (If-None-Match)
  API-->>E: 304
  E-->>U: HTML (references app.9f3c1a.js)

  par Static and media
    U->>E: GET /static/app.9f3c1a.js
    E-->>U: 200 (immutable, 1y)
  and
    U->>E: GET /media/img_8841.avif
    E->>Sh: Miss
    Sh->>S3: Origin fetch (OAC)
    S3-->>Sh: 200
    Sh-->>E: 200 (cached)
    E-->>U: 200
  and
    U->>E: GET /api/trending (s-maxage=30, SWR=60)
    E-->>U: 200 from edge cache
  end

  U->>E: GET /api/home (private, no-store, with auth)
  E->>API: Pass through (warm connection)
  API->>DB: Read from replica
  DB-->>API: Timeline rows
  API-->>E: 200 (Cache-Control: private)
  E-->>U: 200 (not stored at the edge)
Every concept, where it appears
Concept
Where it shows up
DNS and Anycast routing (Part 2)
Bob lands on a nearby PoP, not Virginia.
Cache keys and classification (Part 3)
Assets, media and trending are cached; the timeline is not.
Cache headers (Part 4)
HTML uses no-cache with ETag, JS is immutable for a year, trending uses s-maxage plus stale-while-revalidate.
Origin lockdown (Part 5.5)
The media bucket is private and reachable only through OAC.
Versioned URLs (Part 6.2)
A new deploy changes app.9f3c1a.js, so zero purging for assets.
Tag purge (Part 6.6)
Deleting tweet 101 purges the tweet-101 tag after the database commit.
Private content (Part 7)
The personalised timeline is never stored on the shared cache.
Shield and collapsing (Part 8)
A viral image costs the origin one fetch, not one per PoP.
Read replicas ([relational database guide](/blog/relational-databases-explained), Part 7)
Uncached reads hit a replica, and the CDN reduces how many reads even get that far.

Part 10 — The Interview Playbook (Senior Architect Perspective)

10.1 A seven-step framework for any 'add a CDN' question

Walk through these in order, out loud. It shows structured thinking, and it covers everything an interviewer might probe.

Seven steps
Step
What you say
1. Motivate with numbers
State the read/write ratio, payload sizes, user geography, and the latency or egress cost you are reducing. Example: 300M DAU, 70% of bytes are images and video, users on five continents.
2. Classify content
Static immutable, static mutable, public dynamic, private dynamic, writes. Different strategy for each (Part 3).
3. Choose the model
Pull CDN with an object-store origin. Mention push only for large known libraries.
4. Define the key and headers
What is in the key, TTLs per class, s-maxage vs max-age, ETag, stale-while-revalidate and stale-if-error (Parts 3, 4).
5. Define invalidation
Versioned URLs first, short TTL plus SWR second, tag purge on write events third, purge-all as a break-glass with a stampede plan (Part 6).
6. Protect origin and data
Origin lockdown, shield and collapsing, signed URLs for private media, WAF and rate limits, cache-poisoning awareness (Part 7).
7. Observe and fail safely
Hit ratio, origin load, regional latency; multi-CDN or DNS failover; stale-if-error; graceful behaviour if the CDN control plane is down (Part 8).

10.2 Back-of-envelope capacity thinking

Example estimation
Quantity
Reasoning
Peak egress
100M concurrent viewers at roughly 3 Mbps is about 300 Tbps. No origin can serve that, so the CDN is the only option and the design question becomes cost and cache efficiency.
Origin load at 95% hit ratio
If edge traffic is 1M req/s, 5% misses means 50K req/s to the shield or origin. At a 99% hit ratio it is 10K req/s. Each point of hit ratio is large in absolute terms, which is why cache-key hygiene matters.
Working set vs cache size
Popularity is Zipf-like: a small fraction of objects serves most traffic. Long-tail objects (old tweets' images) cause misses and need the shield and cheap origin storage.
Cost
Dominated by egress (per GB), then requests (per 10K), then purge and compute. Mitigate with a higher hit ratio, compression, image optimisation, committed-use pricing and multi-CDN negotiation.

10.3 Trade-offs you should be able to argue

Decision table
Decision
Trade-off
Long vs short TTL
Long: high hit ratio, low cost, stale risk. Short: fresher, more origin load. SWR gets most of both.
Purge vs versioned URL
Purge is flexible but eventually consistent, rate-limited and does not clear browsers. Versioned URLs are instant and global but need build or storage conventions, and do not fit URLs that must stay stable (like /u/alice).
Tag vs URL purge
Tags need origin cooperation and provider support, but scale to complex dependencies. URL purge is simple, but you must enumerate every affected URL.
Soft vs hard purge
Soft: no stampede, brief staleness. Hard: guaranteed removal (legal takedown), possible origin spike.
Single vs multi-CDN
Single: simple, one config, deeper feature use. Multi: resilience and leverage, at higher operational complexity.
Cache dynamic API data or not
1–5 second micro-caching removes most read stampedes at almost no staleness cost, but needs careful key design for personalisation.
Edge compute vs origin logic
Edge: lowest latency, but limited runtime, harder debugging, data-locality issues. Origin: full power, higher latency.

10.4 Failure modes to raise before they ask

What breaks and how you respond
Failure
Response
Origin down
stale-if-error serves old content; negative caching protects the origin on recovery; health-checked failover to a secondary region.
CDN provider or PoP outage
Anycast and DNS steering move traffic off a bad PoP. For a whole-provider outage you need multi-CDN with health checks and low DNS TTLs, accepting a cold-cache period.
Bad deploy cached everywhere
Fix forward and purge. For assets, roll back by pointing HTML at the previous hashed files; a short HTML TTL makes rollback fast.
Stampede after purge or expiry
Collapsing, shield, SWR, and jittered TTLs (add 0–10% random time so objects do not all expire together).
Hot key
One viral object overwhelms a cache node. Providers replicate hot objects within a PoP; for extreme cases use multiple URL aliases or tiering.
Control plane unavailable
The data plane keeps serving. Make purges queue and retry (the event-driven worker in Part 6.6).

10.5 Common mistakes

Mistakes worth avoiding
Mistake
Why it hurts
Caching HTML for a long time without versioning
Users stay on an old app version, and a purge cannot fix it because their browsers hold it.
Cookies or auth headers in the cache key
Every user gets a separate entry, and the hit ratio collapses to near zero.
Letting responses with Set-Cookie be cached
One user's session cookie can be served to others, which is a security incident.
Purge-all as the default deploy step
You discard a warm cache on every release and invite a stampede. Hash the assets instead.
Forgetting the browser cache
A CDN purge does not reach browsers. Use short max-age plus s-maxage, or versioned URLs.
Purging before the database commit
The edge re-caches the old data right after you purge.
Unrestricted origin
Attackers find the origin IP and skip your CDN, WAF and rate limits.
Ignoring query-string handling
Tracking parameters create endless variants; or the key ignores the query string and your ?v=2 cache buster silently does nothing.
Never testing the miss path
Everything looks great at 99% hits until a purge or failover, when the origin has never been load-tested for a cold cache.

10.6 Interview questions with strong short answers

Common questions
Question
Short answer
What is a CDN and why use one?
A geographically distributed cache and proxy layer that serves content near users, cutting latency, offloading the origin, absorbing attacks and improving availability. (Part 1)
How does a request know whether to hit the CDN or the origin?
It does not decide. DNS points the hostname at the CDN, and the CDN decides hit, miss or pass-through from its cache key and path rules. (Part 2.1)
How does a user get routed to the nearest edge?
GeoDNS or latency-based DNS answers with a nearby PoP, and/or Anycast advertises one IP from many PoPs so BGP picks the closest. (Part 2.2)
What is a cache key and why does it matter?
It defines which requests share one cached object. A bloated key (cookies, tracking params) wrecks the hit ratio; a too-narrow key can serve the wrong content. (Part 3.1)
Difference between max-age and s-maxage?
max-age applies to all caches including browsers; s-maxage applies only to shared caches like a CDN and overrides max-age there. (Part 4)
Difference between no-cache and no-store?
no-cache allows storing but forces revalidation before reuse; no-store forbids storing at all. (Part 4)
How would you invalidate everything after a bad deploy?
Use the provider's purge-all (CloudFront '/*', Cloudflare purge_everything), but first ask whether versioned assets already make it unnecessary; then protect the origin with shield, collapsing and a staged purge to avoid a stampede. (Part 6)
How do you avoid invalidation entirely for static assets?
Content-hashed file names, a long immutable TTL, and a short-TTL HTML entry point that references them. (Part 6.2)
How do you invalidate a dynamic object that appears on many URLs?
Surrogate keys or cache tags: label responses at the origin, and purge the tag on the write event after the database commit. (Part 6.6)
Why does a CDN purge not fix users seeing old content?
Browser caches are separate and untouched by purges; stale content persists until max-age ends. Use shorter browser TTLs or versioned URLs. (Part 6.1)
What is a cache stampede and how do you prevent it?
Many simultaneous misses for one key overload the origin. Prevent with request collapsing, origin shield, stale-while-revalidate, TTL jitter and soft purge. (Parts 6.7, 8)
How do you cache personalised content safely?
Do not cache it on shared keys. Use private or no-store, split the page into a cacheable shell plus a personalised API call, or use a per-user key with signed cookies for specific cases. (Part 3)
How do you secure paid or private media?
A private origin (OAC) plus signed URLs or cookies with short expiry, validated at the edge. (Part 7)
How do you measure whether the CDN is working?
Cache hit ratio (request and byte), origin offload, regional p95 and p99 TTFB, origin 5xx, egress cost and purge latency. (Part 8.3)

Part 11 — One-Page Cheat Sheet

If you remember nothing else
Topic
Rule of thumb
Mental model
Local stores (edges) stocked from one warehouse (origin). Decide what to stock, for how long, and how to recall it.
Who picks CDN vs origin
DNS sends users to the CDN. The CDN picks hit, miss or pass-through.
Static assets
Hashed file names, public, max-age=31536000, immutable.
HTML
no-cache with ETag, so it always revalidates and points at the newest assets.
Public API
s-maxage of seconds plus stale-while-revalidate and stale-if-error.
Private data
private, no-store. Never in a shared cache key.
Enable it
Split static/dynamic, fix headers, create distribution and rules, add TLS, lock down the origin, then switch DNS.
Invalidate
Version URLs, else short TTL plus SWR, else tag purge after the DB commit, else purge-all with a shield.
Browsers
Purges never reach them. Do not give them anything you may need to change.
Protect
Origin lockdown, signed URLs, no Set-Cookie caching, watch for poisoning.
Measure
Hit ratio, origin request rate, regional p95 latency, egress cost, purge latency.

Part 12 — CDN FAQ

Quick answers to lingering questions
Question
Answer
Do I need a CDN for a small app with local users?
Often not at first. Gains are largest for global audiences, heavy static or media payloads, and traffic spikes. Even then, a free-tier edge in front gives you TLS, DDoS protection and caching with almost no effort.
Can a CDN cache API responses?
Yes, for public, idempotent GETs. Use a short s-maxage with stale-while-revalidate and a careful cache key. Never cache responses that depend on credentials unless the key and headers guarantee isolation.
Does a CDN cache POST requests?
Normally no. POST is not cacheable by default and is treated as a write.
How long does an invalidation take?
Usually seconds to a few minutes, varying by provider, plan and scope. It is asynchronous and eventually consistent across PoPs, so design for that.
Is CDN invalidation free?
Not always. Some providers give a monthly free quota and then bill per path; others include unlimited purges but rate-limit them. Check current pricing.
What is the difference between a CDN and a reverse proxy?
A CDN is a globally distributed network of reverse-proxy caches. A single reverse proxy (Nginx) in your data center is the same idea without the geographic spread.
Is it OK to cache HTML?
Yes, if it is public and you control freshness: a short TTL or revalidation with ETag, plus SWR. Static-site and ISR frameworks do this routinely. Avoid it for personalised pages.
What if a URL must stay the same but its content changes (like /u/alice/avatar)?
Use a short TTL with SWR and ETag revalidation, plus a tag or URL purge on update. Or redirect the stable URL to a versioned immutable URL.
Why does this guide put invalidation after setup but before security and scaling?
Because freshness is the first correctness problem anyone hits with a cache. Once content can be wrong or stale, protecting and scaling it are the next questions.

References and Further Reading

  • RFC 9111: HTTP Caching
  • RFC 5861: HTTP Cache-Control Extensions for Stale Content
  • MDN: Cache-Control header
  • AWS: Invalidate files in CloudFront
  • Cloudflare: Purge cache
  • Related guide: Relational Databases Explained: From Tables to Sharding
Up next2/2
Part 1 · Foundations
←
← Prev Chapter
Relational Databases Explained
22 min
Part of a free guide

System Design Course

From relational tables to sharding — ACID, indexes, transactions, locking, and scaling with one running example.

Browse All Guides →
On this page
  • How to Read This Guide
  • Part 1 — The Problem: Why Distance Hurts, and What a CDN Is
    • 1.1 An intuition first
    • 1.2 The vocabulary
    • 1.3 What a CDN actually fixes
  • Part 2 — The Life of a Request
    • 2.1 Step 1: DNS decides, not the URL
    • 2.2 Step 2: Finding the nearest healthy edge
    • 2.3 Steps 3 to 5: connect, look up, answer
    • 2.4 Hit vs miss, side by side
    • 2.5 Two common setups
  • Part 3 — Deciding What Gets Cached
    • 3.1 The cache key
    • 3.2 What to cache and what never to
  • Part 4 — Controlling Freshness with Cache-Control
    • 4.1 The directives that matter
    • 4.2 Defaults for our system
    • 4.3 Revalidation: cheap refreshes with ETag
    • 4.4 stale-while-revalidate: nobody waits
  • Part 5 — Turning It On: How to Enable a CDN
    • 5.1 Pull or push
    • 5.2 The enablement checklist
    • 5.3 Example A: AWS CloudFront in front of S3
    • 5.4 Example B: Cloudflare (proxy mode)
    • 5.5 Locking down the origin
    • 5.6 Verify it works
  • Part 6 — Keeping Content Fresh: Cache Invalidation
    • 6.1 Mental model: there are three layers of cache
    • 6.2 The ladder of preference
    • 6.3 The full menu of techniques
    • 6.4 Commands: invalidating all of it
    • 6.5 Pattern 1: the clean deploy (zero purge for assets)
    • 6.6 Pattern 2: tag-based purge for dynamic content
    • 6.7 What happens when you purge everything
  • Part 7 — Securing the CDN
  • Part 8 — Making It Fast, Cheap and Measurable
    • 8.1 Origin shield and tiered caching
    • 8.2 The levers
    • 8.3 Observability: what to watch
  • Part 9 — The Full Picture: One Page Load in Our Twitter-Like App
  • Part 10 — The Interview Playbook (Senior Architect Perspective)
    • 10.1 A seven-step framework for any 'add a CDN' question
    • 10.2 Back-of-envelope capacity thinking
    • 10.3 Trade-offs you should be able to argue
    • 10.4 Failure modes to raise before they ask
    • 10.5 Common mistakes
    • 10.6 Interview questions with strong short answers
  • Part 11 — One-Page Cheat Sheet
  • Part 12 — CDN FAQ
  • References and Further Reading