SysDesignPrep.com
System design interview question

Design a CDN

A multi-tenant CDN for 30 M customer domains: 300 PoPs, 50 M requests a second served mostly from cache, and global purges in seconds.

Last updated 2026-10-26. Difficulty: hard. Patterns: cdn, anycast, tiered-cache, cache-invalidation, multi-tenancy. Reported at Cloudflare, Akamai, Amazon, Google, Microsoft, Netflix.

Walk through a strong candidate's answer, turn by turn.

The interviewer asks, the candidate answers and draws, and you press Next. Skip to any stage or key moment, and ask the AI Mentor anything along the way.

Or sit it in the style of Amazon · Google · Microsoft · Netflix, run as that company’s round is reported to go.

The short answer

How to design CDN in a system design interview, in five steps:

  1. Scope it. Route every user to a nearby PoP, serve cacheable content from cache, fetch misses with minimal origin load, purge globally within seconds, HTTPS on customers’ own domains, self-serve configuration, absorb traffic spikes and attacks, keep serving when the origin fails.
  2. Set the targets. Scale: 50 M req/s · 40 Tbit/s at peak; Latency of a hit: RTT to PoP p50 < 25 ms · < 5 ms inside; Origin offload: ≥ 95 % of cacheable requests; Purge propagation: p50 < 500 ms · p99 < 5 s.
  3. Do the numbers. Requests per PoP at peak: ~170 k/s average; Peak egress: ~40 Tbit/s; Cache servers: ~5 000 (~17 per PoP).
  4. Draw the design. Steering DNS, L4 load balancer, Edge proxy, PoP cache, Upper tier (shield), Customer origin, Certificate store, Edge config store, Purge bus.
  5. Go deep where it is hard.
    • Anycast or DNS steering → Anycast for everything, in several prefix groups, plus BGP tuning and a DNS overlay where measurements say anycast is wrong.
    • Which server inside the PoP → L4 balancing to every server, then rendezvous hashing of the cache key to an owner, with hot keys copied everywhere.
    • Upper tiers, coalescing and stale content → Per-origin upper tier: edges send misses to the upper-tier PoP nearest the origin, with a backup, and both levels coalesce.
    • Cache keys, RAM, SSD and eviction → Second-hit admission with a Bloom filter, segmented LRU, RAM over SSD.
    • Global purge in under a second → Sequenced pub/sub to an agent in every PoP, a tag index per server, zone generations, acknowledgements, and the origin-fetch time carried downstream.
    • Config and certificates for 30 M domains → Versioned replicated store on every server, with a fast customer lane and a staged platform lane.

Functional requirements

  • Route every user to a nearby PoP. One of 300 points of presence, usually within 20 to 30 ms. Anycast by default: every PoP announces the same addresses and the internet’s routing picks the nearest. A PoP that fails or fills up stops receiving traffic within a minute.
  • Serve cacheable content from cache. Images, scripts, video segments and API responses with cache headers, honouring the origin’s Cache-Control, s-maxage and Vary, with per-customer rules that override them (TTLs, which query parameters matter).
  • Fetch misses with minimal origin load. A popular object should be fetched from a customer’s origin about once per expiry, not once per PoP or per server: a cache hierarchy plus request coalescing. Uncacheable requests are proxied on pooled connections.
  • Purge globally within seconds. By URL, by tag (surrogate key) or everything for a domain. A hard purge deletes; a soft purge marks content stale so it is revalidated. The API says when every PoP has applied it.
  • HTTPS on customers’ own domains. Terminate TLS at the edge for 30 M domains: certificates issued and renewed automatically or uploaded by the customer, chosen per handshake by SNI. TLS 1.3, HTTP/2 and HTTP/3.
  • Self-serve configuration. Origins, cache rules, redirects, WAF and rate-limit settings through an API and a dashboard. A customer’s own change is live worldwide in seconds; changes that affect every customer roll out in stages.
  • Absorb traffic spikes and attacks. Flash crowds and DDoS at layers 3, 4 and 7, from multi-terabit floods to HTTP floods. An attack on one customer must not slow down the others sharing the same servers.
  • Keep serving when the origin fails. Serve the last good copy (stale-if-error) while the origin is down, fail over to a backup origin where one is configured, and do not knock the origin over again as it recovers.
  • Out of scope. Edge compute (Workers-style functions), image and video transformation, the content of WAF rules, analytics and billing pipelines, hosting customers’ DNS zones, and the design of the private backbone between PoPs.

Non-functional requirements

  • Scale (50 M req/s · 40 Tbit/s at peak). Across 300 PoPs and 30 M customer domains, on 100 Tbit/s of capacity. The headroom is deliberate: it is what absorbs a lost PoP or a multi-terabit attack on top of the daily peak.
  • Latency of a hit (RTT to PoP p50 < 25 ms · < 5 ms inside). Being close is the product. TLS 1.3 adds one round trip (none on resumption), so everything inside the PoP on a hit, from the config lookup to reading the object, must cost a few milliseconds at most.
  • Origin offload (≥ 95 % of cacheable requests). This drives the hardest trade-off in the design. 300 PoPs put users close but split every object’s demand 300 ways, so PoPs that each miss on their own multiply origin load. Hashing inside the PoP, an upper tier and coalescing exist to win back the hit ratio that proximity costs.
  • Purge propagation (p50 < 500 ms · p99 < 5 s). A stale price or a legal takedown that keeps serving is a correctness bug for the customer. A purge must never be lost: every PoP acknowledges it, and a PoP that cannot catch up stops serving.
  • Availability (99.99 % per domain · no global outage). A PoP, server or disk failure is routine and must be invisible beyond a reconnect. A network-wide outage must be structurally hard, which mostly means controlling how changes are deployed.
  • Change safety (blast radius: one customer or a few PoPs). The largest CDN outages on record came from a change pushed everywhere at once, not from hardware. A customer’s own change may be fast; anything that touches every customer is staged with health gates and a global stop.
  • Tenant isolation (one domain cannot degrade another). 30 M customers share every server, cache and address range. Per-domain limits on requests, origin fetches and CPU, and cache keys scoped by zone, so one customer’s content can never be served for another’s.
  • Security of keys and content (keys encrypted at rest · nothing private cached). Customers’ private keys sit on thousands of servers in other people’s buildings, so they are encrypted at rest and decrypted only inside the TLS process. Responses marked private, or carrying Set-Cookie, never enter a shared cache.

Back-of-envelope estimates

  • Requests per PoP at peak: ~170 k/s average. 50 M req/s ÷ 300 PoPs ≈ 167 k/s. PoPs are far from equal: the largest 30, in big metros and at exchange points, carry about half the traffic at ~830 k/s each, and the smallest serve a few thousand. Capacity is planned per PoP, for its own peak plus a failed neighbour’s.
  • Peak egress: ~40 Tbit/s. 50 M req/s × ~100 KB average response = 5 TB/s = 40 Tbit/s. Requests are mostly small (scripts, API responses, thumbnails); bytes are mostly large (video segments, images). Against 100 Tbit/s of capacity that is 2.5× headroom, which is what absorbs a lost PoP or a multi-terabit attack on top of peak.
  • Cache servers: ~5 000 (~17 per PoP). For small objects TLS and HTTP processing, not the network card, is the limit: about 20 k req/s per server at full CPU. 50 M ÷ 20 k = 2 500 servers at 100 %; run at 50 % so a PoP can take a failed neighbour’s load or an attack: ~5 000 servers, each doing ~10 k req/s and ~8 Gbit/s at peak. The average PoP has ~17; the largest have 100 or more.
  • Distinct content in one PoP: ~400 TB, not ~20 TB. A 20-server PoP with 20 TB of SSD per server. If every server caches whatever its own connections ask for, all of them hold roughly the same popular set and the PoP stores about one server’s worth, ~20 TB, while a long-tail object is fetched upstream once per server that sees it. Hashing each cache key to one owner server makes the disks add up: 20 × 20 TB = 400 TB. That 20× is the case for hashing inside the PoP.
  • Cacheable requests reaching origins: ~1.1 M/s (97.5 % offload). 90 % of 50 M req/s are for cacheable objects: 45 M/s. Edges hit 90 %, so 4.5 M/s go to upper tiers, which hit 75 % of those: ~1.1 M/s reach origins. Without the tier origins would see 4.5 M/s, 4× more, and the extra misses are exactly the long tail an origin is worst at serving. The other 5 M/s (uncacheable) pass through the tier on pooled connections.
  • Purge fan-out: ~600 k PoP deliveries/s. At a peak of 2 k purge requests a second (publish hooks from customers’ CMSs are most of them), each must reach all 300 PoPs: 2 k × 300 = 600 k deliveries a second. Batching every 20 ms per PoP turns that into 300 × 50 = 15 k messages a second on the relays, and inside a PoP a tag purge touches only the objects its index lists. Purge is a fan-out problem, not a storage one.
  • Configuration on every server: ~60 GB. 30 M domains × ~2 KB of compiled rules (origins, cache rules, WAF and rate-limit settings) = 60 GB, kept on every server’s SSD in a memory-mapped store so a lookup is a local read. Changes run at ~1 k/s at peak. Small enough to replicate everywhere, which is what removes any remote call from the request path.
  • Certificate renewals: ~250 k/day. About 15 M certificates cover the 30 M domains (wildcards and multi-name certificates share), ~75 GB with chains. On 90-day certificates renewed at day 60: 15 M ÷ 60 ≈ 250 k a day, ~3/s, spread with jitter to stay inside CA rate limits. The CA/Browser Forum has voted to cut maximum lifetimes to 47 days by 2029, which roughly doubles this, so issuance must be fully automatic.

Components

  • End users (browsers · apps · bots): Browsers and apps loading 30 M customers’ websites, plus crawlers and attackers that look the same at first. They never know a CDN is involved: they resolve the customer’s hostname and connect to whatever address comes back. Their network’s routing, not their geography, decides which PoP they reach.
  • Steering DNS (anycast · latency maps): Authoritative DNS for the names customers CNAME to us. For most networks it returns the shared anycast address and lets BGP pick the PoP; for networks the mapper has flagged, it returns a regional address group chosen from measured latency, health and load. It is itself served over anycast, so it is as hard to take down as the edge.
  • L4 load balancer (ECMP · XDP · Maglev hash): Runs on every server in the PoP: routers spread packets with ECMP, and an XDP program hashes the 5-tuple to the server that owns the connection, with a connection table so a routing change does not break flows in progress. It is also the first DDoS filter, dropping fingerprinted attack packets before the kernel spends anything on them. Replies go straight back to the user (direct server return).
  • Edge proxy (TLS · HTTP/3 · rules): Terminates TLS and HTTP on whichever server received the connection, reads the domain’s config locally, applies WAF and rate limits, computes the cache key and fetches the object from the PoP server that owns that key. Any server can serve any customer, so the PoP is one pool of CPU. Per-zone limits keep one customer’s flood from using another’s share.
  • PoP cache (RAM + SSD · owner by hash): Each object lives on one owner server per PoP, chosen by rendezvous hashing of the cache key, so the PoP’s disks add up instead of all holding the same objects; only hot keys are copied to every server. The hottest objects sit in RAM and warm ones on SSD, written only on a second request so one-hit wonders do not churn the disk. The owner coalesces concurrent misses into one upstream fetch and keeps a tag index for purges.
  • Upper tier (shield) (per-origin · coalescing): About 15 large PoPs whose caches sit between the edges and customer origins. Each origin is assigned the upper-tier PoP nearest to it, plus a backup, so misses from all 300 edges converge on one cache that coalesces them into a single origin fetch. It keeps warm connections to origins, caps concurrency per origin and serves stale copies when an origin fails.
  • Customer origin (their servers · buckets): The customer’s own web servers, API or object-storage bucket: the source of truth for content and for its Cache-Control and Surrogate-Key headers. Usually sized for a small fraction of the traffic the CDN serves. It should accept requests only from our address ranges or authenticated pulls, or attackers will simply go around the edge.
  • Certificate store (per PoP · keys encrypted): Every customer certificate and its private key, about 15 M certificates and 75 GB, replicated to each PoP with keys encrypted at rest. A server loads a certificate on the first handshake that names it (SNI) and keeps hot ones in memory, so a cold name costs a local read, not a call across an ocean. OCSP staples travel with the certificate where the CA still offers OCSP.
  • Edge config store (versioned KV · every server): A memory-mapped local copy of every domain’s compiled configuration, about 60 GB, on every server, applied from a replicated log in sequence order. Request-time lookups never leave the machine. Each server keeps the previous version of every entry and reports the version it runs, which makes instant rollback and “is it live everywhere?” possible.
  • Purge bus (relays · sequence · acks): A durable, sequenced purge log fanned out by about ten regional relays over persistent streams to an agent in every PoP, edges and upper tiers alike, batched every 20 ms. PoPs acknowledge the highest sequence they have applied, so the control plane knows when a purge is complete and a reconnecting PoP replays exactly what it missed.
  • Traffic mapper (RTT · health · load): Aggregates sampled TCP round-trip times per client network and PoP, plus each PoP’s health and load, into maps of which PoP serves each network best. Where anycast routes a network badly or a PoP runs hot, it changes DNS answers for steerable names and drives BGP changes such as withdrawing one prefix group at one PoP. It is how routing problems become visible at all.
  • Config distributor (replicated log · two lanes): Compiles each change, tests it against the real proxy build in a sandbox, appends it to a replicated log and streams it to every server’s config store and every PoP’s certificate store. A customer’s change takes a fast lane, live worldwide in seconds and reverted automatically if that zone’s errors rise; changes every customer runs take a staged lane (canary, 1 %, 10 %, 100 %) with health gates between stages.
  • Control plane API (config DB · versioned): The customer-facing API and dashboard for domains, origins, rules, certificates and purges. Backed by a strongly consistent database in which every change to a zone gets the next version number, so the state of every server can be named exactly. It is never on the request path: if it is down, the edge keeps serving with the config it has.
  • Certificate manager (ACME · renewals · OCSP): Issues certificates for new domains through ACME with at least two CAs, proving control with HTTP-01 tokens served by our own edge or DNS-01 records, and renews every certificate at two thirds of its life, about 250 k a day. Fetches OCSP responses for CAs that still provide them so the edge can staple them, and alerts on any certificate within 14 days of expiry.
  • Certificate authorities (ACME · two or more): Public CAs that sign certificates after validating control of the domain. They have their own rate limits and outages, so issuance is spread across more than one and retried with backoff. Lifetimes are shrinking (47-day maximums by 2029), which makes renewal volume a design input.

User flows

  1. Load an image: hit or miss. The main read path. Anycast picks the PoP, any server terminates TLS, one owner server per object answers from RAM or SSD, and a miss climbs to the origin’s shield before it reaches the origin. Every hop is chosen by a different hash, and each one exists to keep the hit ratio up.
    1. The browser resolves the customer’s hostname and gets a shared anycast address. The customer’s img.shop.com is a CNAME to a name we serve. For most networks the answer is the same anycast address everywhere with a 5-minute TTL, because BGP will do the steering; only networks the mapper has flagged get a regional address group instead. DNS is not on the per-request path after the first lookup.
    2. The connection lands at the nearest PoP and is spread across its servers. BGP delivers the SYN or QUIC Initial to the topologically nearest PoP, about 20 ms away for most users. Routers spread packets with ECMP, and the XDP balancer on each server hashes the 5-tuple Maglev-style and keeps a connection table, so every packet of the connection reaches the same server even if ECMP reshuffles. Replies leave directly from that server, so the balancer only carries the small inbound half.
    3. That server terminates TLS with the certificate for the requested name. The ClientHello’s SNI says which of 30 M domains this is. The certificate and key come from an in-memory LRU, or on first use from the PoP’s encrypted certificate store in about a millisecond, after which the server keeps it hot. TLS 1.3 costs one round trip and none on resumption, and ticket keys are shared across the fleet so resumption works at any PoP.
    4. The proxy reads the domain’s configuration and computes the cache key. Config is a local read from the memory-mapped store on the same server, at a known version: origin, cache rules, WAF and rate limits. The cache key is the zone, host, normalised path and the query parameters the rules keep, sorted, plus one normalised value per header the response varies on (Accept-Encoding becomes br, gzip or identity). Tracking parameters such as utm_* are dropped.
    5. The proxy hashes the key to the server in the PoP that owns it and asks that server. Rendezvous hashing over the PoP’s healthy servers puts each object on exactly one of the 40, so the PoP holds 40 servers’ worth of distinct content instead of one. The owner answers from RAM for hot objects or from SSD (~100 µs) for warm ones, and the hop inside the PoP costs well under a millisecond. Keys above ~1 k req/s in the PoP are copied to every server and served locally.
    6. On a miss, the owner holds every concurrent request for the key on one fetch from the origin’s shield. Forty browsers asking for a new image in the same second become one request out of the PoP, because they all hash to one owner and the owner keeps one in-flight fetch per key. The request goes to the upper-tier PoP assigned to this origin, which runs the same hashing and coalescing across the misses of all 300 edges.
    7. The shield fetches from the origin over a warm connection, and both tiers store the response by its headers. The shield keeps pooled connections to busy origins, so a miss pays no TCP or TLS handshake. Cache-Control decides storage: s-maxage for us, private or no-store never stored, and the Surrogate-Key tags recorded for purge. The time the shield fetched the object from the origin travels with it to the edge, and purge decisions compare against that time.
    8. The owner streams the bytes to every waiting request and decides whether the object earns a place on disk. Waiters receive bytes as they arrive rather than after the whole object lands, so coalescing never delays a large file. The object is kept in RAM, but written to SSD only if a rotating Bloom filter has seen its key in the last few hours, which keeps one-hit wonders off the disk. The response reaches the browser with Age set, from the server that holds the connection.
  2. Add a domain and a cache rule. The control-plane write path. A customer adds a domain, gets a certificate without doing anything, and a rule change reaches every server in seconds. The same machinery that makes changes fast is what could take every customer down at once, so validation and per-zone rollback are part of the path, not an afterthought.
    1. The customer adds shop.com and a cache rule through the API. The control plane writes to a strongly consistent config database, and every change to the zone gets the next version number. Validation runs before anything is accepted: schema, limits such as rules per zone and pattern size, and that the origin resolves.
    2. Once the customer’s DNS points at us, the certificate manager orders a certificate. ACME needs proof of control: an HTTP-01 token that our own edge serves under /.well-known/acme-challenge, or a DNS-01 record when we host the zone, which wildcards require. Orders go to at least two CAs, so one CA’s outage or rate limit does not stop onboarding. We generate the ECDSA key, and it never leaves the certificate system unencrypted.
    3. The certificate is stored with the zone and replicated to every PoP’s certificate store. About 15 M certificates at ~5 KB with chains is ~75 GB, small enough to replicate to every PoP, which keeps any remote call out of the handshake. Keys are encrypted at rest with a key only the TLS-terminating process can unwrap. Renewal runs at two thirds of the lifetime with jitter, about 250 k renewals a day.
    4. The distributor compiles the new config version and runs it through the real proxy in a sandbox. Compiling the rules to the binary form the proxy reads, then replaying synthetic requests through the production proxy build, catches configs that parse but misbehave: a pattern that backtracks, a redirect loop, a rule that crashes a worker. This is the customer lane: its blast radius is one zone, so it does not wait hours for a staged rollout.
    5. The version is appended to the replicated log and every server’s local store applies it. Relays stream the log to every PoP, and every server applies entries in sequence order, so it is always at one consistent position. Propagation takes a few seconds worldwide. Each server reports the sequence it has applied, so the API can tell the customer when the change is live everywhere.
    6. The first visitor’s handshake loads the new certificate on demand. Nothing is pushed into server memory ahead of time: the first ClientHello for shop.com on each server misses the in-memory LRU, reads the certificate from the PoP store and decrypts the key, about a millisecond, and every later handshake on that server is served from memory. That is what lets 15 M certificates live on a fleet that keeps only the hot ones in RAM.
    7. Requests use the new rule, and the zone’s error rate decides whether it stays. Servers report per-zone error and crash counts alongside the version they applied. If the zone’s 5xx rate or crash count jumps after the change, the distributor reverts the zone to its last good version everywhere within seconds and tells the customer. Changes that touch every customer, such as proxy releases and managed WAF rules, never use this lane: they go canary, 1 %, 10 %, 100 % over hours with health gates.
  3. A 3 Tbit/s flood on one customer. The scale-breaking case for a CDN is an attack, not organic load. Anycast spreads it across the PoPs nearest the bots, every server drops junk at the cheapest layer it can, and per-zone limits keep the other customers on the same servers unaffected.
    1. A botnet floods the customer’s address with reflected UDP and SYN packets. Every PoP announces the same address, so each bot’s packets go to the PoP nearest that bot, and 3 Tbit/s from a botnet spread over many countries arrives as roughly 10 to 100 Gbit/s at each PoP instead of 3 Tbit/s at one. With 100 Tbit/s of capacity and 40 used at peak, the network has about 60 Tbit/s to spare. A per-PoP unicast address would have taken the whole flood at one site.
    2. Each server samples its packets, fingerprints the attack and installs a drop rule in its packet filter. Detection runs on every server against its own samples, so mitigation starts within seconds and does not depend on a central system that may itself be under attack. The rule matches the fingerprint (source port 53, packet length, a TTL pattern) and drops in XDP before the kernel allocates anything, at tens of millions of packets a second per server. SYN floods are answered with SYN cookies, so half-open connections cost no memory.
    3. HTTP floods that complete TLS reach the proxy, which applies the zone’s rate limits and challenges. Layer 7 requests look valid, so they are judged by rate and fingerprint: per client IP, per TLS fingerprint and per path, counted per server and synchronised across the PoP every second, which is approximate and good enough to stop a flood. Suspicious clients get a JavaScript or proof-of-work challenge rather than a hard block. The limits come from the zone’s config plus an attack-mode rule that can be switched on for that zone alone.
    4. Requests for cacheable URLs are answered from cache, and cache-busting variants are cut off at the key. A flood of GETs for /index.html costs a cache hit each, and the origin sees nothing. Attackers add random query strings to force misses, so static paths ignore unknown query parameters in the key, and a per-zone cap on origin fetches per PoP stops whatever misses remain. Coalescing does not help here, because every key is different; the key rule and the cap do.
    5. The mapper sees one PoP running hot and steers what it can away from it. If a PoP near a dense cluster of bots saturates, the mapper first moves what DNS controls: names on regional address groups get answers that avoid that PoP. As a last step the PoP withdraws only the attacked customer’s prefix, which pushes that attack onto neighbours that spread it again, while every other customer keeps being served locally. Withdrawing the whole PoP would move the attack and every innocent user along with it.
    6. Other customers sharing the attacked address are moved, and everyone else keeps normal latency. Junk is dropped before it costs CPU, and each zone’s proxy work is bounded by its limits, so a flood on one zone cannot spend another zone’s share. If one address keeps being targeted, the other zones on it are given different addresses through DNS, so the attacked address ends up carrying only its target. The 50 % utilisation target is what makes all of this possible: headroom is the defence.
  4. The origin goes down: keep serving. The failure path. An origin starts timing out while its content is expiring. With stale-if-error, coalesced revalidation and a cap on origin concurrency, users see slightly old pages instead of errors, and the origin is not knocked over again as it recovers.
    1. A cached API response expires and its owner asks the shield to revalidate it. The response had s-maxage=60, stale-while-revalidate=30 and stale-if-error=86400. Inside the revalidate window the owner serves the stale copy at once and refreshes in the background, so no user waits. The refresh is a conditional request, so an unchanged object costs a 304 rather than a body.
    2. The shield merges every edge’s revalidation into one origin request, which times out. Edges across the world expire the same object within a minute of each other, and the shield turns all of them into one origin request. Origin timeouts are short (2 s to connect, 10 s to first byte), because a slow origin holding thousands of our connections is worse than a quick failure. An error on revalidation never overwrites the cached copy.
    3. The shield serves its stale copy downstream instead of the error. stale-if-error lets a cache use an expired response when the origin fails. We apply it by default for up to a day, unless the origin’s headers or the zone’s config set a different limit. The shield marks the copy as stale so the edge does not treat it as fresh for a full TTL, and the edge rechecks after a short backoff.
    4. Users keep getting the last good response from their own PoP. The product list is a few minutes old, which is far better than a 5xx. Objects with no stale copy (never cached, or private) do fail, and the error is cached for at most a few seconds, so a dead origin is not hammered but a recovered one is used at once.
    5. The shield marks the origin unhealthy, caps its concurrency and fails over if a backup exists. Timeouts per origin and active probes put the origin behind a circuit breaker: a few probes a second test it while everything else is served stale. If the zone has a backup origin, such as a second region or an object-storage copy, misses go there. A per-origin cap on concurrent fetches per shield server stops a thundering herd when the origin comes back.
    6. When the origin recovers, revalidations succeed and fresh copies replace the stale ones. The first successful probes close the breaker gradually, raising the concurrency cap over tens of seconds. Most revalidations return 304, so recovery costs headers, not bodies. Without coalescing and the cap, every edge would retry at the same moment and the origin would go straight back down.
  5. Purge a tag worldwide in a second. The asynchronous path customers judge us on: one API call must stop 300 PoPs serving every object tagged product-123, within a second, with no message lost. Delivery is a sequenced log with acknowledgements, and applying a purge is an index lookup, never a scan.
    1. The customer’s CMS purges the product’s tag after a price change. Tags came from the origin’s Surrogate-Key header when each object was cached, so the CMS does not need to know which URLs show the product. The control plane authenticates the token, rate-limits purges per zone, and stamps the purge with a timestamp and a sequence number in a durable log before acknowledging.
    2. The purge is published to regional relays, which stream it to an agent in every PoP. About ten relays hold persistent streams to all 300 PoPs, so delivery is one hop over an open connection rather than 300 new ones. Purges are batched every 20 ms, which turns ~600 k PoP deliveries a second into ~15 k messages. Every message carries its sequence number.
    3. Every PoP, upper tiers included, marks the matching objects stale through a per-server tag index. Each server keeps an in-memory index from (zone, tag) to the objects it holds with that tag, built from Surrogate-Key as each object was stored, so a tag purge touches only matching objects and never scans a disk. A URL purge goes only to the key’s owner in each PoP, and a purge of a whole zone just bumps the zone’s generation number, which makes every older object stale on read.
    4. Each PoP acknowledges the sequence it has applied, and the control plane marks the purge complete. The purge is complete when all 300 PoPs report a sequence at or past it, typically under 500 ms and under 5 s at p99. A PoP that has not acknowledged is retried; one that loses the feed for more than 60 s withdraws its routes until it has replayed the log, because serving a purged price is worse than serving from the next PoP.
    5. The next request revalidates the stale copy, and a copy older than the purge is never cached again. Each PoP keeps the last minute of purges in a small table. When a copy arrives from upstream, its origin-fetch time (set by the shield and carried downstream) is checked against recent purges for its tags: if an edge raced ahead of a shield that had not applied the purge yet, the old copy is rejected and fetched again with a must-be-newer condition. A soft purge lets the stale copy be served while one coalesced revalidation runs; most come back 304 or with the new price.

Deep dives

Anycast or DNS steering

How does a user reach a nearby PoP? Why not just answer DNS with the closest one?

Every request starts with a choice made before our software sees a byte: which of 300 PoPs receives the packets. DNS steering answers each resolver with one PoP’s own address, chosen from latency maps, health and load. But the answer is cached for its TTL and often longer, the authoritative server sees the resolver rather than the user (EDNS Client Subnet helps only where resolvers send it), and failover takes minutes for a tail of users. Anycast announces the same addresses from every PoP and lets BGP deliver each packet to the nearest one: failover is a route withdrawal, seconds to a minute, but BGP chooses by network path, not by latency or load.

The two fail in opposite ways, and attacks decide between them. With anycast a botnet’s packets are spread over the PoPs nearest each bot, so 300 sites absorb the flood together; with per-PoP addresses, an attacker who resolves the name once can aim everything at one site. So the base should be anycast, and the precision DNS offers should be added on top, only where measurements show anycast routing a network badly.

  • Anycast for everything, in several prefix groups, plus BGP tuning and a DNS overlay where measurements say anycast is wrong chosen
  • DNS steering to per-PoP unicast addresses situational: as the overlay, for networks anycast routes badly and customers with residency rules, answering with a regional anycast group rather than a single PoP
  • Client-side steering from a list of hosts situational: for our own apps or a customer’s video player, as Netflix does on its own CDN
  • One global anycast prefix, no measurement or overlay rejected

The answer: Every PoP announces the same customer-facing prefixes, split into a few groups, to its transit providers, exchange-point peers and directly connected ISPs, and domains get addresses from those groups. The internet’s routing takes each user to a nearby PoP and shares each attack among the PoPs nearest its sources. The traffic mapper measures TCP round-trip times per client network and PoP on real connections; where a network lands more than ~30 ms worse than its best PoP, the network team fixes it with BGP (a direct peering, a community asking the ISP to prefer a route, or a prepend at the wrong PoP), and until then steering DNS answers that network with a regional group announced only from nearby PoPs. To drain a PoP it withdraws one prefix group at a time, so neighbours take the load in steps. Cloudflare runs its whole network on anycast; Akamai’s published architecture (Nygren, Sitaraman and Sun, 2010) steers with a DNS-based mapping system. Both work at scale, and the choice is about what you optimise for: here, attack absorption and simple failover.

A PoP loses power at peak. What happens to the people connected to it?

Its routers stop answering, the BGP sessions drop (within a second or so with BFD, rather than the 90-second default hold time), and traffic for its prefixes moves to the next-nearest PoPs. Connections in progress reset, and browsers retry idempotent GETs on a new connection. Resumption still works because session ticket keys are shared across the fleet, so the new PoP resumes TLS without a full handshake. The neighbours must have the headroom, which is why PoPs run at about 50 % at peak.

Anycast is sending a big ISP’s users in Mumbai to Singapore. How do you notice, and what do you do?

The mapper shows it: for that ISP’s client networks, the PoP they reach is 60 ms worse than the Mumbai PoP other networks in the city reach. The usual cause is that the ISP buys transit from a carrier that hands our routes over in Singapore. The fixes, in order: peer with the ISP directly in Mumbai, ask it via a BGP community to prefer our Mumbai route, or prepend our Singapore announcement toward that carrier. Meanwhile steering DNS can answer that ISP’s resolvers with the India-only group.

Why not withdraw a PoP that is under attack?

Because the attack follows the route: withdrawing moves the flood to the next PoP, and every innocent user of the withdrawn PoP goes with it. It is better to drop the attack where it lands, since dropping a packet in XDP costs almost nothing. If one PoP near a dense cluster of bots is truly saturated, withdraw only the attacked customer’s prefix there, so the attack spreads over the neighbours while other customers stay local.

Does anycast break long-lived connections?

It can, when a route changes mid-connection and packets start arriving at a PoP that has no state for them. In practice routes are stable for far longer than a typical request, and the mapper alerts on prefixes that flap. Long downloads resume with Range requests, and WebSocket clients reconnect. Some CDNs move very long sessions to a PoP-specific address with a redirect, which trades anycast’s protection for stability.

How would you know routing is working?

Per client network, compare the RTT to the PoP actually reached with the best PoP measured for that network, from TCP samples on real connections and from beacons in a small share of pages that fetch a test object from several PoPs. The headline is the share of requests served within 10 ms of their best PoP. Alongside it: load per PoP against capacity, BGP route changes per prefix, and how long a withdrawal takes to move traffic.

Which server inside the PoP

A PoP has 40 servers. Which one handles a request, and why not just round-robin connections?

A large PoP takes several hundred thousand requests a second across 40 or more servers. Round-robin by connection spreads CPU evenly, but each server then caches whatever its own connections ask for, so all 40 hold roughly the same popular set. The PoP stores about one server’s worth of distinct content (20 TB instead of 800 TB), and a long-tail object is fetched from upstream once by every server that happens to see it.

Two decisions are being made, and they want different answers. Who terminates the connection should be spread evenly, because TLS is the CPU cost. Who stores the object should be exactly one server per key, because disk is the capacity. Separate them: an L4 balancer spreads connections over every server, and the proxy that terminates TLS hashes the cache key to its owner and fetches across the PoP’s network. The one exception is a key so hot that its owner cannot carry it.

  • Round-robin or least-connections; every server caches what it serves rejected
  • A dedicated L7 balancer tier that routes by URL hash rejected
  • L4 balancing to every server, then rendezvous hashing of the cache key to an owner, with hot keys copied everywhere chosen
  • Hash the client’s IP to a server rejected

The answer: Routers spread incoming packets over the PoP’s servers with ECMP, and an XDP program on every server hashes each packet’s 5-tuple through a Maglev-style table to choose the server that terminates the connection, with a connection table so flows survive ECMP or membership changes; replies go straight to the user. The proxy on that server computes the cache key and uses rendezvous hashing over the PoP’s healthy servers to find the owner, which serves from RAM or SSD and alone goes upstream on a miss. Membership comes from a per-PoP health checker and is versioned, and a server must fail checks for about 5 seconds before its keys move; a moment of disagreement only costs a duplicate copy, never a wrong answer. A count-min sketch on each server flags keys above ~1 k requests a second in the PoP, and those are copied into RAM on every server. Google’s Maglev and Facebook’s Katran are public examples of this kind of L4 balancer, and consistent hashing itself came out of the research Akamai was founded on (Karger et al., 1997).

A cache server dies. What happens to its keys and its connections?

Its connections reset and clients reconnect; the L4 table sends them to other servers. Its keys, about 1/40 of the PoP’s objects, now rank another server first, which misses and fetches them from the upper tier rather than the origin, so the cost is a short bump in tier traffic. When it comes back it rejoins with a low weight that rises over minutes, so its keys move back gradually instead of all becoming misses at once.

One image gets 300 k requests a second in Frankfurt. What breaks?

At 100 KB each that is 240 Gbit/s from one owner, several times what one server’s network card carries. The count-min sketches on the servers receiving those requests see the key cross the threshold within a second, and each keeps its own RAM copy, fetched once from the owner. The load then spreads over all 40 servers at about 6 Gbit/s each. When the key cools, the copies age out of RAM on their own.

Why rendezvous hashing rather than a ring with virtual nodes?

For 40 to 200 servers, scoring every server for a key is cheap and needs no virtual-node tables, it spreads keys evenly, and removing a server remaps only that server’s keys. It also gives a natural ranking, so “the top two servers for this key” is free when a hot key needs a second owner. A ring wins when there are thousands of members and scoring each becomes the cost, which a PoP never has.

What does the extra hop inside the PoP cost?

Latency is a few hundred microseconds on the PoP’s switches, invisible next to a 20 ms user round trip. Bandwidth is the real cost: with 40 servers, about 39 in 40 hit bytes cross the PoP network from owner to terminating server before going out, so the fabric is sized for roughly twice the PoP’s egress. That is far cheaper than the alternative, 20 to 40 times more misses.

Why not have the owner send the response straight to the user?

Because the TLS session lives on the server that terminated the connection, and handing a live TLS connection between servers mid-request is complex and fragile. Direct server return already keeps the balancer out of the response path, which is the big saving. The hop from owner to terminating server is the price of separating CPU from disk, and it is worth paying.

Upper tiers, coalescing and stale content

Each of 300 PoPs misses on its own. How do you keep a customer’s origin from melting, including when it is already down?

Each PoP sees about 1/300 of an object’s demand. An object requested 3 000 times an hour worldwide is requested about 10 times an hour per PoP, so after every expiry each PoP that sees a request fetches it again: up to 300 origin fetches per expiry instead of one. A purge of a busy site, or a popular object expiring everywhere at once, turns that into a thundering herd, and the origin is usually sized for a few percent of the traffic we serve for it.

The fix is to aggregate demand before it reaches the origin. If every edge sends its misses for an origin to one upper-tier cache, that cache sees the combined demand of all 300 edges, hits far more often, and with coalescing turns any number of simultaneous misses into one fetch. And when the origin fails, an expired copy is nearly always better than an error.

  • Edges fetch straight from the origin rejected
  • Per-origin upper tier: edges send misses to the upper-tier PoP nearest the origin, with a backup, and both levels coalesce chosen
  • Several regional tiers per origin situational: for the largest customers, whose miss traffic is more than one tier PoP should carry
  • Push or pre-warm content to every PoP situational: a customer pre-warms a known launch, such as a game patch, through an API

The answer: Each origin gets a primary and a backup upper-tier PoP from about 15 large PoPs, chosen by the mapper as the lowest RTT to the origin, much as Cloudflare’s Smart Tiered Cache picks one upper tier near each origin (Fastly calls the same idea shielding). Edge misses go to that tier, where the same rendezvous hashing picks one owner server, so a burst of misses from 300 edges for one object makes one origin fetch, and waiters receive bytes as they arrive. Uncacheable requests also pass through the tier on pooled connections, which saves the origin a TLS handshake per request. stale-while-revalidate is honoured so users never wait on a refresh, and stale-if-error applies by default for up to a day unless the origin or the zone’s config says otherwise, so an origin outage becomes slightly old content. Each tier server caps concurrent fetches per origin (say 64), and a failing origin goes behind a circuit breaker that lets only probes through until it recovers.

Doesn’t the tier add latency to every miss?

Hits are untouched. For a miss, the edge-to-tier leg replaces the edge-to-origin leg and covers most of the same distance, because the tier is near the origin, and it runs over warm pooled connections, so there is no fresh TCP and TLS handshake to the origin. Measured end to end, a tiered miss is often as fast as a direct one. What it costs is a second copy of the object and one more component on the miss path.

The upper-tier PoP for a big origin fails.

Edges see failed fetches and switch to the backup tier within seconds. The backup is cold for that origin, but coalescing bounds the cost at about one origin fetch per object, and edges serve stale copies of anything that expires meanwhile. The mapper then picks a new primary. A tier failure becomes a short rise in that origin’s load, not a herd.

How does coalescing handle a response that turns out to be uncacheable?

The waiters cannot share a private response, so each is released to fetch on its own, and the key is remembered as uncacheable for a few minutes (hit-for-pass) so later requests skip the queue instead of being serialised one behind another. Without that, a popular uncacheable URL would be fetched one request at a time. The same applies to responses with Set-Cookie unless the zone’s rules strip it.

A 2 GB file is requested and someone seeks to the middle.

Large objects are cached in fixed slices, say 2 MB, each with its own cache key and the same validators. A Range request fetches only the slices it covers, so a seek costs a few slices rather than the whole file, and the slices of one file spread across servers by hashing. Coalescing works per slice, so viewers of the same video segment share fetches.

How do you know the hierarchy is working?

Per customer: edge hit ratio, tier hit ratio, and origin requests as a share of edge requests, which is the offload the customer pays for. The coalescing ratio, requests served per upstream fetch, shows whether bursts collapse. And origin fetches per object per expiry should sit near one per tier, not near the number of PoPs; when it climbs, a cache key is usually varying on something it should not.

Cache keys, RAM, SSD and eviction

What exactly is the cache key, and with 20 TB of disk per server and an endless long tail, what do you keep?

The key decides correctness and hit ratio at once. Too coarse and a user gets the wrong content: another customer’s file, a gzip body for a client that cannot read it, the mobile page on a desktop. Too fine and the hit ratio collapses: a tracking parameter, Vary on User-Agent or a cookie in the key turns one object into thousands. The disk has its own problem: most objects are requested once. Akamai’s published analysis of its traces (Maggs and Sitaraman) found that about three quarters of objects were one-hit wonders, and writing each of them to SSD evicts something useful and wears the drive.

The answers are normalisation and admission. Build the key only from what the customer’s rules say matters, normalised into a few values; keep the hottest objects in RAM and the warm ones on SSD; and write an object to SSD only once it has been asked for twice.

  • Plain LRU, every miss written to SSD rejected
  • Second-hit admission with a Bloom filter, segmented LRU, RAM over SSD chosen
  • Frequency-based admission (TinyLFU) situational: for the RAM tier of small objects, where a slightly better hit ratio is worth the counters
  • Separate pools by object size situational: a server serves both large media and small web objects heavily

The answer: The key is (zone, host, normalised path, the query parameters the zone’s rules keep, sorted), plus one normalised value per header the response varies on: Accept-Encoding becomes br, gzip or identity, an image Accept becomes avif, webp or jpeg, and Vary on User-Agent or Cookie is uncacheable unless the customer maps it to a small set such as mobile and desktop. Each server has 512 GB of RAM, about half of it holding the hottest objects, and 20 TB of SSD written in large log-structured segments, so writes are sequential and eviction drops whole segments. An index in RAM of ~100 bytes per object covers ~200 M objects in ~20 GB. On a miss the object is served and held in RAM, but written to SSD only if a rotating Bloom filter has seen its key in the last few hours; eviction is segmented LRU in RAM and CLOCK over SSD segments. Akamai reported that caching on the second request cut disk writes sharply while slightly improving hit rates, because one-hit wonders stopped evicting useful objects.

A customer’s origin sends Vary: User-Agent. What do you do?

Taken literally, every browser version gets its own copy and the hit ratio falls to almost nothing. By default we treat the response as uncacheable at the edge and tell the customer why. If they map User-Agent to a device class (mobile, tablet, desktop) in their rules, the key carries that class and the origin receives a normalised header, so three copies replace thousands.

Someone requests /logo.png?x=<random> 100 k times a second.

Every request is a new key and a miss, so it goes upstream and the origin pays. For static file extensions the default key ignores query strings unless the customer lists parameters that matter, which turns the flood into hits. Beyond that, a per-zone cap on origin fetches per PoP and a rate limit on clients with a high miss rate stop the rest. Coalescing does not help, because no two keys match.

Why not keep everything in RAM?

RAM costs something like 50 to 100 times more per gigabyte than SSD, and the hot set is small: a few percent of bytes serve most hits. An SSD read takes about 100 µs, which disappears next to a 20 ms round trip to the user. So RAM holds the hottest objects and the index, and SSD holds the warm long tail.

An object’s TTL expires. Do you delete it?

No. The TTL decides freshness, not residency. An expired object stays until eviction removes it, because revalidating it costs a 304 and a few headers instead of the whole body, and because it is exactly what stale-if-error serves when the origin is down. Eviction is driven by recency and frequency alone.

How do you stop one customer from filling every disk?

Cache space is not reserved per customer, because that would waste most of it, but admission and eviction are fair by construction: an object stays only if it keeps being requested, and a customer whose objects are all one-hit wonders never gets past admission. For extreme cases there is a cap on the bytes any one zone may hold per server, and large plans can buy a reserved share.

Global purge in under a second

A customer changes a price and purges. How does every PoP stop serving the old page within a second, and how do you know it did?

At 2 k purges a second, each delivered to 300 PoPs, the system makes 600 k deliveries a second, and a lost one is a correctness bug: the old price keeps serving until its TTL runs out, which may be a day. The objects a tag refers to are scattered over thousands of servers that each hold ~200 M objects, so finding them by scanning is out of the question. And there are two tiers: if an edge drops its copy and refetches from an upper tier that has not yet heard about the purge, it caches the old content again with a fresh lifetime.

Three ideas make it work. Delivery is a sequenced log fanned out over persistent streams, so every PoP can say exactly how far it has applied and catch up after a gap. Finding objects is an index lookup, not a scan: each server maps tags to the objects it holds, and a purge of a whole domain is just a new generation number. And freshness is judged against the time the object left the origin, which the tier passes downstream, so an old copy cannot sneak back in.

  • Short TTLs instead of purging situational: content that changes on a schedule, where a 30-second TTL with stale-while-revalidate is good enough
  • Broadcast each purge to every server and scan for matches rejected
  • Sequenced pub/sub to an agent in every PoP, a tag index per server, zone generations, acknowledgements, and the origin-fetch time carried downstream chosen
  • Purge rules checked on every read situational: for prefix purges, kept per zone until the zone’s longest TTL has passed

The answer: The control plane stamps each purge with a sequence number and a timestamp, writes it to a replicated log and returns 202 with an id. About ten regional relays stream the log, batched every 20 ms, to a purge agent in every PoP, upper tiers included, and each PoP acknowledges the highest sequence it has applied. Inside a PoP, a URL purge goes to that key’s owner; a tag purge goes to every server, which looks up (zone, tag) in its in-memory index and marks those objects stale (soft) or drops them (hard); and a purge of everything bumps the zone’s generation so older objects are stale on read. Every object carries the time the upper tier fetched it from the origin, and each PoP keeps the last minute of purges, so a copy older than a recent purge of one of its tags is rejected and fetched again on condition that it be newer. A PoP that loses the feed for more than 60 seconds withdraws its routes until it has replayed the log, trading a little latency for never serving content its customer has purged. Fastly’s surrogate keys and soft purge (it says purges reach its network in about 150 ms) and Cloudflare’s Cache-Tag purge are public versions of the same product.

A PoP is cut off from the purge feed for three minutes, then comes back.

After 60 seconds without the feed’s heartbeat it withdrew its anycast routes, so its users were served by neighbours in the meantime. On reconnect it asks its relay for everything after its last applied sequence; the log is kept for hours, so the replay is complete. Only when it has caught up does it announce its routes again. The trade-off is deliberate: a PoP slightly further away is better than one that might serve a purged price.

A customer purges everything on a site doing 50 k requests a second. What happens to the origin?

The purge itself is one generation bump per PoP, so it applies instantly, and then every object is stale at once. With a soft purge, edges keep serving stale copies while revalidating, coalescing at both levels makes it about one conditional request per object at the origin, and most come back 304. With a hard purge it is one full fetch per object, and the per-origin concurrency cap at the tier spreads that over a minute or two instead of a second.

Why mark objects instead of deleting them?

A soft purge marks rather than deletes because a marked object can still be revalidated with a 304, or served if the origin fails, which a deleted one cannot. A hard purge drops the index entry and leaves the bytes for normal eviction to reclaim, since rewriting SSD segments on every purge would be expensive. Either way no disk is ever scanned.

What if the purge reaches an edge before its upper tier?

The edge marks its copy stale, the next request revalidates against the tier, and the tier, still unpurged, returns the old copy. That copy carries the time it was fetched from the origin, which is older than the purge the edge has applied, so the edge rejects it and asks again with a must-be-newer condition, which sends the tier to the origin. Ordering between tiers stops mattering, which is simpler and faster than purging tiers first and edges second.

How do you prove purges are fast, not just claim it?

Two measurements. From the acknowledgements: the time until all 300 PoPs report the purge’s sequence, as p50 and p99 per region. And end to end: a synthetic zone changes an object every few seconds, purges it, and probes it from every PoP until each returns the new version, which also catches a PoP that acknowledged but applied wrongly. Both sit on the dashboard.

Config and certificates for 30 M domains

Customers want their changes live everywhere in seconds. How do you get 30 M configurations and their certificates to every server without one bad change taking down the network?

Every request needs its domain’s configuration (origin, cache rules, WAF and rate limits) and every handshake its certificate, in microseconds, so both must already be on the server: about 60 GB of config and 75 GB of certificates, changing around a thousand times a second. The danger is the push itself. Cloudflare’s 2 July 2019 outage came from a WAF rule whose regular expression backtracked and exhausted CPU, deployed everywhere at once; Fastly’s 8 June 2021 outage came from a valid customer configuration that triggered a latent bug in a software release, and took most of its network down for under an hour.

Two kinds of change carry different risks and deserve different paths. A customer’s change affects one zone, so it can be fast if it is validated and isolated; a change to software, or to rules every zone runs, affects everyone and must be staged with health gates. Both are versioned, so every server can say exactly what it runs and roll back to its last good version without asking anyone.

  • Central config service queried per request, with a cache rejected
  • Replicate everything to every server and apply each change everywhere at once rejected
  • Versioned replicated store on every server, with a fast customer lane and a staged platform lane chosen
  • Load a domain’s config on its first request from a regional store situational: for the far tail of domains that see almost no traffic, alongside full replication for active ones

The answer: The control plane’s database gives every change to a zone the next version; the distributor compiles it to the binary form the proxy reads, checks limits, accepts only linear-time (RE2-style) regex engines, and replays synthetic requests through the production proxy build in a sandbox. Accepted changes are appended to a replicated log that relays stream to every server, which applies them in order to a memory-mapped local store and keeps the previous version of each entry; Cloudflare’s Quicksilver is a public example of a replicated store on every server. A customer’s change is live everywhere in a few seconds, and if that zone’s error rate rises the distributor reverts it automatically. Proxy releases, managed WAF rules and other global changes go first to canary PoPs carrying employee and free traffic, then to 1 %, 10 % and 100 % over hours, with automatic halts on error, CPU or crash regressions and a global stop that does not depend on the system being changed. New software is also run against a corpus of real customer configurations before it ships, the test that catches a bug a valid config can trigger.

How do you serve TLS for 30 M domains from any server?

The ClientHello’s SNI names the domain, and the server looks for its certificate in an in-memory LRU, then in the PoP’s certificate store, where all ~15 M certificates are replicated with keys encrypted at rest. A cold name costs about a millisecond, once per server, and hot names stay in memory. ECDSA certificates are preferred because they are small and cheap to sign with, with RSA kept for old clients. Customers who will not hand over a key can keep it on their own key server and have the edge ask it to sign each handshake, as Cloudflare’s Keyless SSL does.

Issuing and renewing 15 M certificates: what goes wrong?

CAs have rate limits and outages, so the certificate manager uses at least two and falls back on failure. Validation fails when the customer’s DNS does not point at us yet, so orders retry with backoff and the dashboard says why. Renewals start at two thirds of the lifetime with jitter, about 250 k a day, and any certificate within 14 days of expiry raises an alert. Lifetimes are shrinking toward 47 days by 2029, which roughly doubles the volume, so none of this can be manual.

Do you staple OCSP?

Where the CA offers OCSP, yes: the certificate manager fetches each certificate’s OCSP response before the previous one expires and distributes it with the certificate, so the edge staples it into the handshake and the browser never has to ask the CA. That saves the user a round trip and the CA a flood of queries. Some CAs, Let’s Encrypt among them, have dropped OCSP in favour of revocation lists, and for those there is nothing to staple; short lifetimes do much of the same job.

A customer’s valid config makes the proxy crash on one request in a hundred. Contain it.

Crashes are counted per zone: a worker that dies while handling a zone records it, and after a handful in a minute the zone is reverted to its last good version everywhere and frozen against further changes until someone looks. Workers restart in milliseconds, and other zones’ in-flight requests are retried on another worker. A crash caused by a valid config is a platform bug, so it also opens an incident against the release, and the config joins the test corpus.

How do you know what every server is running?

Every server reports, with its heartbeat, the log sequence it has applied for each lane and its software version. The dashboard shows propagation lag per PoP at p50 and p99, and flags any server more than a few seconds behind. A server whose apply fails keeps its last good version and alerts, and if it falls minutes behind it takes itself out of service rather than serve old rules.

The theory behind it

  • CDNs and edge computing: How a content delivery network works, push vs pull, cache keys and TTLs, what to put at the edge, edge functions and KV, and the pitfalls of caching dynamic content.
  • Global traffic management, DNS and anycast: How users reach the nearest healthy region: DNS resolution and TTLs, GeoDNS and latency-based routing, anycast, global load balancers, health checks and failover, the limits of DNS-based failover, and steering traffic during incidents and migrations.
  • HTTP caching and cache headers: How browsers, CDNs and proxies decide what to cache: Cache-Control directives, max-age and s-maxage, ETags and conditional requests, immutable assets with fingerprinted URLs, stale-while-revalidate, Vary, private versus shared caches, and purging.

Related

Report an issue

Something wrong with this question? A number that does not add up, a step whose answer key looks off, a typo. It goes straight to the author with the page you are on.

Watch a strong candidate answer thisTurn by turn, with the design drawn as they talkWalk through →Mock interview