Art of Codeartofcode
LLeeaarrnniinngg
BBlloogg
TToooollss
GGaammeess AArreennaa
JJoobbss
LLiivvee CCoouurrsseess

Courses

  • System DesignHigh-level system design, one everyday scene at a time: 30 concepts and 15 interview rounds.712 lessons
All coursesTutorialsMy Learning

Categories

  • 01AI & Tools
  • 02Tutorials
  • 03Engineering
  • 04Reviews
  • 05Build Logs
  • 06Tales of Misfits
  • 07Opinion
  • 08Career
  • 09News

Read & write

All storiesEvery post, newest first.Write for usPublish your stories and earn from what you write.Writer leaderboardsWho is writing the most read posts.

Categories

  • PDF Tools25
  • Development09
  • Utilities10
  • Design04
  • Converters04
  • Generators02

Featured

JSON FormatterFormat, validate and minify JSON.Regex TesterTest patterns against live text.Merge PDFCombine PDFs in your browser.JWT DecoderRead a token’s header and claims.Password GeneratorStrong passwords, generated locally.Gradient GeneratorBuild CSS gradients visually.
All 53 tools

Play a round

DevdleOne developer word a day, six guesses.Python RushClassic snake, with coding icons to eat.Blink or BreakA quick test of your reflexes.
Games Arena

Learn with us

Live CoursesInstructor-led courses. Join a waitlist and be first in when one starts.LearningStructured programming courses with AI assistance and certificates.

System Design

26. CDNs

Lesson 14 of 16

26. CDNs

1
The picture to keep in your head
2
26.1 Why use a CDN?
3
26.2 What are edge locations (PoPs)?
4
26.3 Pull vs push CDN: what's the difference?
5
26.4 How do cache keys and headers work?
6
26.5 How does purging work?
7
26.6 What is an origin shield?
8
26.7 Can the CDN handle dynamic content?
9
26.8 How do CDNs deliver video (HLS, DASH)?
10
26.9 What happens when the CDN fails?
11
Memory card and where the analogy breaks
12
Practice: five levels
13
Interview questions
14
Solutions
15
Sources
16
Code lab
26. CDNs

Solutions

3 min read+10 XP

Solutions

Level 1

reveal

Level 2

reveal

Level 3

reveal

Level 4

reveal

Level 5 (sketch)

reveal

No tracks available

Lofi Player

artofcode

artofcode.in

The den of devs.

Stories, tools, games, and gear for developers.

Read the StoriesEExxpplloorree TToooollss
Art of Codeartofcode

Platform

  • BBlloogg
  • TToooollss
  • GGaammeess AArreennaa
  • CCoouurrsseess
  • LLeeaarrnniinngg
  • CCaammppuuss

Company

  • AAbboouutt UUss
  • JJoobbss && IInntteerrnnsshhiippss
  • CCoonnttaacctt

Legal

  • TTeerrmmss ooff SSeerrvviiccee
  • PPrriivvaaccyy PPoolliiccyy
  • RReeffuunndd PPoolliiccyy

Copyright © 2026 artofcode.in, All Rights Reserved.

IST·--:--

1Show the Level 1 solution
2**A.**
31. `public, max-age=31536000, immutable`.
42. `private, no-store`.
53. `public, max-age=60, s-maxage=300, stale-while-revalidate=60, stale-if-error=600`.
64. `private, no-store`.
75. `public, s-maxage=5` (micro-cache for a few seconds).
8
9**B.**
101. **False.** Personal responses can't be shared; the CDN only helps the connection.
112. **False.** It creates one cache entry per user and leaks risk; serve personal parts separately.
123. **False.** Browsers keep their copy until their own max-age ends.
134. **True.**
145. **True.** They're ordinary HTTP files.
1Show the Level 2 solution
21. 6 × 60 = **360 ms** to Mumbai; 6 × 8 = **48 ms** to the local PoP.
32. 10,000 × 0.05 = **500** a second at 95%; 10,000 × 0.01 = **100** at 99%.
43. 2,000,000 × 1.5 MB = **3 TB** a day served; origin: 5% of that = **150 GB**.
54. 1,200 ÷ 6 = 200 per quality × 5 = **1,000** files. One 720p segment: 2.8 Mbps × 6 s = 16.8 Mb ÷ 8 = **2.1 MB**.
65. **50** without a shield, **1** with it.
1Show the Level 3 solution
2Tested solution (Python 3), the key parts:
3
4```python
5def cache_key(url):
6    parts = urlsplit(url)
7    params = [(k, v) for k, v in parse_qsl(parts.query)
8              if not k.startswith(("utm_", "fbclid", "gclid"))]
9    query = urlencode(sorted(params))
10    return f"{parts.netloc.lower()}{parts.path}" + (f"?{query}" if query else "")
11
12
13def cdn_ttl(header):
14    cc = parse_cache_control(header)
15    if cc.get("no-store") or cc.get("private"):
16        return None                          # never in a shared cache
17    if "s-maxage" in cc:
18        return cc["s-maxage"]                # shared caches use this first
19    return cc.get("max-age")
20
21
22class Edge:
23    def get(self, url, now, origin_up=True):
24        key = cache_key(url)
25        hit = self.store.get(key)
26        if hit and now < hit[1]:
27            return hit[0], "HIT"
28        if not origin_up:
29            if hit and now < hit[1] + hit[3]:
30                return hit[0], "STALE"       # stale-if-error
31            return None, "ERROR"
32        body, header, tags = self.origin(url)
33        ttl = cdn_ttl(header)
34        if ttl is not None:
35            stale = parse_cache_control(header).get("stale-if-error", 0)
36            self.store[key] = (body, now + ttl, tags, stale)
37        return body, "MISS"
38
39    def purge_tag(self, tag):
40        for k in [k for k, v in self.store.items() if tag in v[2]]:
41            del self.store[k]
42```
43
44All checks pass: 50 tracking variants collapse to 1 key; s-maxage, private and no-store are respected; the edge hits, expires, serves stale while the origin is down and then errors; a tag purge drops a book page and its cover together; a shield cuts 50 origin fetches to 1; the video ladder picks 720p at 4 Mbps; failover routes to the healthy CDN; and the public account page is served to the wrong user while the private one isn't. The full file is in `code/ch26_cdn.py`.
1Show the Level 4 solution
21. **What happened:** the account page is HTML, so it got `public, max-age=300`. Asha requested `/account`; the CDN cached **her** page under the key `/account` (cookies aren't in the key). Ravi requested `/account` within 5 minutes; the CDN found a fresh copy and served **Asha's page** to Ravi. The tested demo reproduces exactly this.
32. **The fix:** cache only pages that are the same for everyone, and mark personal ones private:
4
5```python
6PUBLIC_HTML = ("/", "/books/", "/authors/", "/categories/")
7
8@app.after_request
9def add_caching(response):
10    if response.mimetype != "text/html":
11        return response
12    if request.path.startswith(PUBLIC_HTML) and not current_user.is_authenticated:
13        response.headers["Cache-Control"] = "public, max-age=60, s-maxage=300"
14    else:
15        response.headers["Cache-Control"] = "private, no-store"
16    return response
17```
18
19Better still, **default to `private, no-store`** and opt public pages in. Two other places the old rule harms: the **cart and checkout** pages (other users' carts, payment forms), and any page with a **CSRF token or flash message** baked in (one user's token served to others breaks forms and security). Also, logged-in users seeing a "Hi Asha" header on book pages.
203. **Right now:** **purge** `/account` and every personal path (or purge everything if unsure), **disable** the rule in CDN config (most CDNs can override origin headers to bypass caching for paths), then treat it as a **security incident**: work out which users' pages could have been exposed and follow your disclosure process.
1Show the Level 5 (sketch) solution
21. **Caching rules:**
3   - Versioned JS/CSS and covers (`cover-42-v3.jpg`): `public, max-age=31536000, immutable`.
4   - Book and author pages (anonymous view): `public, max-age=60, s-maxage=600, stale-while-revalidate=60, stale-if-error=3600`, tagged `book-{id}`, `author-{id}`.
5   - Homepage: `public, max-age=30, s-maxage=30` normally; micro-cached at `s-maxage=5` during sales.
6   - Account, cart, checkout, APIs under `/me`: `private, no-store`.
7   - Public JSON (`/api/books/42`): like book pages.
82. **Cache keys:** host + path + sorted query parameters, dropping `utm_*`, `fbclid`, `gclid`; `Vary: Accept-Encoding` only; device-specific images handled by separate URLs or edge image resizing, not Vary on User-Agent.
93. **Changes:** price or details change → purge tag `book-{id}`; cover change → new versioned URL (no purge); takedown → purge by tag and URL plus 410 from origin; personal greeting fetched client-side so pages stay shareable.
104. **Big Book Sale:** origin shield in Mumbai with request collapsing; homepage and listing micro-caching; pre-warm top pages; stale-while-revalidate so refreshes never block; edge rate limits and bot protection (Chapter 25).
115. **Video and outages:** HLS with 6-second segments in 5 qualities, stored in object storage behind the CDN, segments cached long, playlists short. For a CDN outage: a second CDN with DNS-based health steering for static assets and video, stale-if-error for origin trouble, CDN config managed as code with staged rollout.