- Light in optical fiber covers about 200 km per millisecond, so a reader in Sydney can't get an answer from a server in London in less than 170 ms, and real routes take about 270.
- Loading a page takes many round trips: the TCP and TLS handshakes, then the page, then the files it names, each sent in growing bursts. Distance multiplies every one, which is why latency matters more than bandwidth.
- A content delivery network puts copies of the site on servers near readers. Most requests become short local round trips, and only the misses travel to the origin.
- How well that works depends on popularity, freshness, cache keys, how readers are steered to an edge, and caches stacked behind caches. Even pages that can't be cached load faster through an edge, which keeps warm connections open to the origin.
Put a website on a server in London and open it from Paris, and it appears before you've finished clicking. Open the same page from Sydney, on a connection just as fast, and you wait two or three seconds watching a blank tab. Nothing is wrong with the server or the line. The page is just far away.
Here are both readers loading the same page, a small article with a stylesheet, a script, four images, and a font, from the same server in London. The bars run in real time. Let it play, then turn on Use a CDN:
Paris is done in under 0.2 seconds and Sydney in about 2.6, and on the globe you can see why: every orange and black dot is a message crossing 17,000 km of ocean and continent, and Sydney can't move on until each reply comes back. With the CDN on, Sydney talks to a server in its own city and finishes in about 0.12 seconds, sooner than Paris.
That server is part of a content delivery network, or CDN: hundreds of machines in cities around the world, each holding copies of other people's websites. This page is about how that works. It starts with the speed of light, because that is the one part nobody can engineer away, and it ends with the tricks edge servers use for the pages they can't keep copies of at all.
Distance
An answer from a server has to travel to you and back. Over long distances it travels as pulses of light in glass fiber, and light in glass is slower than in a vacuum: glass has a refractive index of about 1.5, so light covers about 200 km in a millisecond instead of 300 . I've put the origin, the server that holds the real site, in London. Drag the reader anywhere on the map, or jump to , , or :
From Paris the light is back in under 4 ms. From New York it takes 56, and from Sydney, about as far from London as you can get on land, 170 ms. Those are the round-trip times, or RTTs, for a perfect cable laid along the shortest path over Earth's surface, with nothing in the way. No faster hardware will beat them; the only way to shorten them is to shorten the distance.
Real cables don't follow great circles. They run along coasts, around continents, and through cable landing stations, and every router on the way adds a little time. WonderNetwork pings between its servers around the world every hour , and I took the typical round trips from London to eight cities. Step through them and compare each measurement with the straight-line minimum:
Every dot sits above the orange line. New York, with straight cables under the Atlantic, comes closest at 70 ms against a minimum of 56. Sydney takes 270 ms, 1.6 times its minimum, and Tokyo 2.4 times, because its traffic to Europe goes the long way round. From here on, when I need a round trip between two arbitrary places, I use the : 1.6 times the straight-line minimum, Sydney's own factor, plus 10 ms for the reader's own connection. That makes Sydney to London about 280 ms.
Light will cross the Pacific no faster next year than this year. Bandwidth has grown a thousandfold in a few decades; the speed of light hasn't changed at all. So the question is how many round trips a page needs.
Handshakes
Before a browser can ask for a page, it has to set up a connection, and every setup step is a question that waits for an answer. To draw the waiting, I'll use a ladder diagram: the reader and the server are vertical lines, time runs down the page, and every message is an arrow that slants because it takes time to arrive. Here is the reader in Sydney connecting to London, 280 ms apart. Step through the messages, then switch the protocol:
With TLS 1.2, the reader sends SYN, waits for SYN-ACK, and only then starts the encryption handshake, which takes two more round trips. The request goes out after three round trips, 840 ms, and the first byte of the page arrives at 1,120 ms. Nothing in that time was limited by the line's speed. Each arrow simply had to wait for the one before it.
The TCP handshake makes sure both ends are listening. The TLS handshake agrees on encryption keys and lets the server prove who it is with a certificate. folds the key agreement into a single exchange , saving one round trip, and , the transport under HTTP/3, does the transport and encryption setup together in one . Across 280 ms, each saved round trip is a quarter of a second. And this was only the first answer: a page needs more than one, and big answers come in pieces.
TCP doesn't know how much the network between the two ends can carry, so it starts cautiously. The first burst of a reply is 10 packets of about 1,460 bytes, roughly 14 KB . When those are acknowledged it sends 20, then 40, doubling every round trip until something gets lost. This is slow start. Drag the file size and count the round trips:
A 14 KB file fits in the first burst and costs one round trip. A image takes three bursts, 10, 20, and the last 39 packets, so three round trips. A needs seven. The bursts only grow wider on the diagram when the bandwidth starts to matter: at 50 Mbit/s, a burst of 320 packets takes 75 ms just to send.
The Sydney reader upgrades from a 10 Mbit/s line to 1,000 Mbit/s, a hundred times faster. How much sooner does a 100 KB image from London arrive on a new connection?
Almost all of the 1.45 seconds is round trips: two for the handshakes, one for the request and the first burst, and two more while slow start opens up. Only the last burst waits on the line's speed. At 10 Mbit/s it takes about 45 ms to send, at 1,000 Mbit/s under 1 ms, so the upgrade saves about 45 ms.
Here is the whole cost of fetching one 100 KB file on a new TLS 1.3 connection, split into time spent waiting for round trips and time spent actually sending bytes. Slide the bandwidth, then the round trip:
At 280 ms the curve is flat across almost the whole range. From 10 to 1,000 Mbit/s the fetch drops from about 1.45 to 1.40 seconds, because the sending part was small to begin with. instead and the same file arrives in about 60 ms. For files of this size, the time it takes is set by the number of round trips times the length of each . Bandwidth only takes over for big downloads, like video, where the bursts have grown wide enough to fill the line.
A page also isn't one file. The browser gets the HTML first and only then learns which stylesheet, script, and images it names. The stylesheet in turn names a font, which can't be requested until the stylesheet arrives. That makes three rounds of discovery after the handshakes, each of which waits for the one before. Slide the round trip and watch the whole load stretch:
Each bar's pale part is the wait for its first byte; the dark part is the bursts arriving. From Sydney the page takes 2.6 seconds, about nine round trips: two for the handshakes and seven more for the three rounds of requests and their bursts. From it takes 190 ms, and the dark parts, the actual sending, become a visible share of the time. The thin gray ticks mark one round trip each, and nearly every bar starts and ends on one.
Edges
If the round trips can't get faster, they have to get shorter. A CDN runs edge servers in many cities, and readers talk to the nearest one instead of the origin. The edge keeps copies of the site's files, so it can usually answer on its own. Here are 20 edge sites. Drag the reader around and compare the two round trips:
From Perth the nearest site is Sydney, 3,200 km away, and the page arrives in about 0.6 seconds instead of 2.2. The nine round trips are still there; each is just a fifth as long. Move the reader to a city with its own site, like , and the round trip falls to the 10 ms of the reader's own connection.
Where should the sites go? I placed these 20 with a simple rule over 48 of the world's largest cities, weighted by their rough populations: start with London, then keep adding whichever city most lowers the average round trip from everyone to their nearest site. Real networks choose sites where internet exchanges and data centers are, but the rule shows the trade-off. Add sites one at a time:
With only the origin in London, the average reader in these 48 cities waits 131 ms per round trip. The second site goes to central China and the third to Bogotá, and by six sites the average is 41 ms. After that the curve flattens: give 18 ms, and the last twenty together save only another 7, because the remaining gaps serve fewer people. The floor is the reader's own connection, which no CDN can shorten. Large CDNs run hundreds of sites anyway; Akamai described more than 61,000 servers in nearly 1,000 networks in 2010 . Being inside the reader's own internet provider, a few milliseconds away, also beats being in the same city.
So far I've assumed the edge already has every file. When it doesn't, it has to fetch the file from the origin, and the reader waits for that too. Here is one 100 KB file requested through the Sydney edge, 10 ms from the reader and 270 ms from London. Switch between a hit and a miss:
A hit is over in about 60 ms: two short round trips for the handshakes and three for the bursts, all inside Sydney. A miss takes 1.4 seconds. The edge has to open its own connection to London, with the same handshakes and the same slow start across the same 270 ms, before it can pass the bursts along. That's no faster than going to London directly. An edge only speeds things up when it already has the file, so the next question is how often it does.
Hits and misses
An edge can't keep a copy of everything; its disks and memory are shared by thousands of sites. When it's full and a new file arrives, something has to go. The usual rule is to evict the least recently used file, the one that has waited longest since anyone asked for it. Here is a tiny edge with a few slots, and requests for 40 files arriving one by one. Lower numbers are more popular files. Watch the hits and misses, then change how popularity is spread:
When , the same handful keep coming back. They stay near the front of the row and almost never fall off, so with 8 slots for 40 files about half of all requests are hits. When , 8 slots hold 8 random files out of 40, and the hit ratio sinks to about a fifth, no better than 8 in 40. The hit ratio isn't a property of the cache alone. It comes from how unevenly readers want things.
Real traffic is very uneven. Lee Breslau and his colleagues found that requests passing through web proxies follow a Zipf-like pattern: the file ranked k is asked for in proportion to 1/k^{\alpha}, with \alpha between about 0.6 and 0.7 in their traces . Here is that curve for a site with a million files. Choose how many of the most popular ones the edge keeps, and see what share of requests they cover:
On log-log axes the popularity curve is a straight line, and the green head looks impressive. But the right-hand plot shows how much lives in the tail. With \alpha = 0.8, keeping the top 10,000 files, 1% of the site, covers only 36% of requests, and keeping 10% of the site covers 61%. Each tenfold increase in cache size adds roughly the same slice, so the hit ratio grows like the logarithm of the cache size . Facebook found the same shape for photos in 2013: the top 1,000 photos drew about a quarter of requests, the next 9,000 another quarter, the next 90,000 a third quarter, and the remaining 2.5 million the last . That pattern is close to .
The tail has a second, sneakier cost. A CDN splits a site's traffic across all its edges, so each edge sees only its own share of the requests. Take a site with a million files getting 1,000 requests a second in total, and say each edge keeps a copy for an hour after fetching it. A copy only pays off if someone at that edge asks for the file again within the hour. Spread the same traffic over more edges:
With , even the file ranked 100,000th is asked for about every 12 minutes, so its copy is usually still fresh, and 83% of requests are hits. With , each edge sees 10 requests a second; any file past rank 2,000 or so is asked for less than once an hour at a given edge, and most of its copies expire unused. The overall hit ratio drops to 29%, though nothing about the site changed. I'll come back to this, because it's what the layers of caches later on are for.
Freshness
A copy is only useful while it matches the original. The origin says how long that is with a header on every response, such as Cache-Control: max-age=600: keep this for 600 seconds . That span is the copy's time to live, or TTL. Here is one file at one edge over two hours. Each miss fetches a copy that lives for one TTL, and the requests in that span are hits. At minute 30 the origin publishes a new version. Slide the TTL:
With a 10-minute TTL, the edge goes to the origin 11 times in two hours and, after the change, serves 6 out-of-date answers over 8 minutes. Push the TTL to and the origin hears from this edge only twice, but readers get the old version for 35 minutes after it changed. At almost every answer is current, and 43 of the 52 requests become trips to London. How long the old version lingers is partly luck: anything up to one TTL, depending on when the edge last fetched its copy. Every TTL is a bet on how often the file changes against how much a stale answer costs.
When a copy expires, the edge doesn't have to download the whole file again. It can ask the origin whether the file changed, sending the version tag it already holds in an If-None-Match header; if nothing changed, the origin answers 304 Not Modified with no body. That still costs a round trip to London. The stale-while-revalidate directive lets the edge skip the wait: it answers from the expired copy at once and checks in the background . Compare the two, and change whether the file was actually updated:
Asking first keeps every answer current, and the reader waits 280 ms, one London round trip plus the hop to the edge. A 304 saves the origin from sending the 40 KB again, which matters for the origin's bandwidth bill more than for the reader. With , the reader is done in 10 ms. If the file , that reader gets the old copy one last time, and the next reader gets the new one.
Which copy
An edge finds its copies by a cache key, by default the full address of the request. Two requests share a copy only if their keys match exactly. That's more fragile than it sounds, because addresses carry parts that don't change the file at all: tracking tags added by newsletters and social sites, or the same search parameters in a different order. Here are 12 requests that reached an edge in a few minutes, really for 4 different files. Change the rules for building the key:
Keyed on the full address, the 12 requests make 9 different keys, so there are 9 misses and only 3 hits, though there are only 4 files. merges the three photo addresses and the two news addresses, and merges the two searches: 4 keys, 8 hits. Now to the key, which is what happens when a site varies its responses by cookie. Every reader gets a private copy, and all 12 requests are misses. Keys can also legitimately grow: a response marked Vary: Accept-Encoding is stored once per compression format, because a browser that can't read Brotli must not get the Brotli copy.
Sometimes a file changes and can't wait for its TTL, say a typo on the home page or a broken script. The site can then purge the file: tell the CDN to drop every copy now. Here are 20 edges holding version 1 of a file with an hour's TTL, each fetched at a different moment. At time zero the origin publishes version 2. Compare waiting with purging:
Waiting, the last edge still serves the old version almost an hour after the change, because its copy was fetched just before it. empties every edge within moments, and each one fetches version 2 from the origin on its next request. That's a burst of misses at once, which is why purging popular files is done with some care. Many sites avoid both problems by putting a version in the file's name, like app.3f9a2c.js. A changed file gets a new name and therefore a new key, so it can be cached for a year, and only the page that names it needs a short TTL or a purge.
Finding the nearest edge
All of this assumes the reader reaches a nearby edge, but the browser only knows the site's name. Something has to turn that name into the address of an edge close to this particular reader. CDNs do it in one of two ways.
The first works through DNS, the internet's directory of namesHow a name becomes an address, step by step, is the subject of a separate page on DNS. Here only the last step matters.. The browser asks a resolver for the site's address, and the resolver asks the CDN's own name server. That server picks an edge and answers with its address. But it sees the question coming from the resolver, not from the reader, so it picks the edge nearest the resolver. Drag the reader and the resolver apart:
The reader is in Sydney, but uses a public resolver whose nearest server is in Singapore. The CDN sees a question from Singapore and sends the reader to Ho Chi Minh City, 120 ms away, while the Sydney edge sits 10 ms away unused. With a resolver run by the reader's own provider, , the guess is right. The fix for public resolvers is an extension called EDNS Client Subnet: the resolver includes the first part of the reader's address in its question, enough to locate the reader's network but not the reader . and the CDN can answer for the reader instead of the resolver.
The second way skips the guessing. With anycast, every edge announces the same address to the internet, and the routers between networks, which speak a protocol called BGP, carry each reader's packets to whichever announcement looks closest. But BGP doesn't measure kilometers. It counts how many networks a route passes through and follows the business deals between them. Here is a reader whose provider is in Auckland, with two carriers to choose from:
The provider in Auckland hears the address through carrier B in two steps and through carrier A in three, so it sends the reader across the Pacific to Los Angeles, about 180 ms away, though Sydney is 45 ms away. Distance never entered the decision. A team at Microsoft measured this on Bing's anycast CDN and found it worked well for most clients, but about 20% were sent to an edge that wasn't their best one . CDNs fix such cases by , which makes their own route the shortest. Anycast also brings a gift: when an edge fails, it stops announcing, and the routers find the next one by themselves. Take the and the reader lands in Los Angeles again, with no change to any DNS answer.
Caches behind caches
Back to the problem from the hit-ratio section: a CDN splits each site's traffic across many edges, so each edge sees too little of the long tail to keep useful copies, and every edge's misses land on the origin. The fix is another layer. Instead of going to the origin, an edge that misses asks a second cache, an origin shield, which serves all the edges. Here is the site from before, a million files, 1,000 requests a second, copies kept an hour. Add edges, then turn on the shield:
With 20 edges, each hits only 45% of the time, so 553 of every 1,000 requests reach the origin. and the origin's share drops to 163. The shield sees the misses of all 20 edges together, so a tail file that each edge sees once a day, the shield sees 20 times a day, often enough to keep a copy. Add and the edges get worse, but the origin barely notices: the shield makes the whole network look like one big cache to it. Breslau's group saw the other side of this in their traces: requests reaching a parent cache followed a flatter popularity curve, because the children had already answered the popular ones . Facebook's photo stack showed the same: its edge caches hit 59% of the time, and the cache behind them still caught a third of what got through .
Shields also help with a sharper problem. When a popular file's copy expires, or a breaking story goes up, many readers can ask the same edge for it within the same fraction of a second. If the edge forwards every one of those misses, the origin gets hit by a crowd. Here are requests for one file arriving at an edge over 300 ms, just after its copy expired. Change how many, then let the edge combine them:
Without collapsing, 20 readers make 20 identical trips to London, each costing the origin the same work. forwards only the first. The readers who come in while it's on its way wait at the edge, shown as green dots, and get the same answer when it arrives; the few who come after it are plain hits. The origin sees one request. A shield multiplies the effect, since it collapses the requests from all the edges as well. The readers who arrived during the wait still pay most of a London round trip, but nobody pays more than that, and the origin stays standing.
What can't be cached
Everything so far works because many readers want the same bytes. A page that greets you by name, shows your basket, or lists your messages is different: it's yours, and handing a cached copy to the next reader would be a bug, possibly a security hole. Such responses are marked Cache-Control: private, and the edge passes them through without keeping a copy. Often only a small part of a page is personal. Here is an article page, with each part colored by who sends it. Sign the reader in, then change how the page is built:
Signed out, every part of the page is the same for everyone, and all of it comes from the edge. Signed in, three small personal details turn the whole 16 KB of HTML purple: the edge can't reuse it, so every page view waits on London. fixes most of that. The shared HTML is cached like any file, and the greeting, basket, and recommendations arrive in a 2 KB request of their own that the page fills in. The article appears from the edge, and only the dashed boxes wait for London.
That small personal request still has to cross the world, and so does any request that can't be cached at all: a search, a login, a payment. Yet even these get faster when they go through the edge, which is less obvious than it sounds. Here is a 16 KB personal page fetched from Sydney three ways:
Straight to London, the page takes about 1,150 ms: two round trips of handshakes, one for the request, and one more because 16 KB is a little more than slow start's first burst. Going saves nothing, since the edge has to do the same handshakes with London. The gain comes from the . Edges keep connections to busy origins open for many requests from many readers, with the encryption already set up and TCP's window already grown . The reader's own handshakes happen over 10 ms to the edge, and only the request and its answer cross the world: 320 ms, a quarter of the direct time. Big CDNs also route these requests over their own backbone, which can beat the public internet's path.
The edge can also make the bytes themselves fewer. Text files such as HTML, CSS, and JavaScript shrink a lot with compression, and the edge can compress them once and keep the compressed copy. Fewer bytes means fewer slow-start bursts, so compression saves round trips, not just bandwidth. I compressed three real files with gzip and with Brotli . Pick a file and an encoding, and count the bursts it needs on a new connection:
Bootstrap's stylesheet is 233 KB as written, 160 packets that need five bursts. Brotli shrinks it to , 16 packets in two bursts. From London on a new connection, that's three round trips, about 0.8 seconds, saved by compression alone. Brotli beats gzip by about a quarter on this file, and the edge can afford its slowest, smallest setting because it compresses each file once and serves the result many times. JPEG photos and video are already compressed, so compressing them again gains almost nothing.
Sydney again
Here is the page from the start of this one, loaded from Sydney, with its HTML now personal so it can't be cached. The stylesheet, script, images, and font can. Switch on the pieces one at a time and watch where the time goes:
With nothing but compression, the page takes 2.6 seconds, as it did at the start. it takes about 0.4. The handshakes and every slow-start burst now happen over 10 ms, the shared files come from the edge's copies, and the personal HTML crosses to London once over the edge's warm connection, which is most of what's left. saves one round trip of handshake, about 0.28 seconds from Sydney, but only about 10 ms through the edge, where round trips are cheap. Compression changes little here, because by the time the big files go, slow start has already opened the window. The order of the gains matches the order of this page: distance first, then everything else.
No CDN can make light faster. What it does is rearrange the work so that almost every round trip a page needs happens over a short distance: handshakes with a nearby edge, files from that edge's copies, misses gathered behind a shield, and the unavoidable trips to the origin sent over a connection that's already open. Some CDNs now also let sites run their own code on the edges, so that more of the personal page can be assembled near the reader. The trip that remains is set by the size of the planet: about 270 ms from Sydney to London and back.
References
- Ilya Grigorik, High Performance Browser Networking, O'Reilly, 2013. Chapters 1 to 4 (latency, TCP, TLS) are free online at hpbn.co.
- WonderNetwork, “Global Ping Statistics”, hourly pings between its servers; London to eight cities, typical averages for October 2026.
- E. Rescorla, “The Transport Layer Security (TLS) Protocol Version 1.3”, RFC 8446, IETF, 2018.
- J. Iyengar and M. Thomson, “QUIC: A UDP-Based Multiplexed and Secure Transport”, RFC 9000, IETF, 2021.
- J. Chu, N. Dukkipati, Y. Cheng and M. Mathis, “Increasing TCP's Initial Window”, RFC 6928, IETF, 2013.
- Erik Nygren, Ramesh K. Sitaraman and Jennifer Sun, “The Akamai Network: A Platform for High-Performance Internet Applications”, ACM SIGOPS Operating Systems Review 44(3), 2010.
- Lee Breslau, Pei Cao, Li Fan, Graham Phillips and Scott Shenker, “On the Implications of Zipf's Law for Web Caching”, Technical Report 1371, Computer Sciences Department, University of Wisconsin-Madison, 1998; published as “Web Caching and Zipf-like Distributions: Evidence and Implications”, IEEE INFOCOM 1999.
- Qi Huang, Ken Birman, Robbert van Renesse, Wyatt Lloyd, Sanjeev Kumar and Harry C. Li, “An Analysis of Facebook Photo Caching”, Proceedings of the 24th ACM Symposium on Operating Systems Principles (SOSP), 2013.
- R. Fielding, M. Nottingham and J. Reschke, “HTTP Caching”, RFC 9111, IETF, 2022.
- M. Nottingham, “HTTP Cache-Control Extensions for Stale Content”, RFC 5861, IETF, 2010.
- C. Contavalli, W. van der Gaast, D. Lawrence and W. Kumari, “Client Subnet in DNS Queries”, RFC 7871, IETF, 2016.
- Matt Calder, Ashley Flavel, Ethan Katz-Bassett, Ratul Mahajan and Jitendra Padhye, “Analyzing the Performance of an Anycast CDN”, Proceedings of the ACM Internet Measurement Conference (IMC), 2015.
- J. Alakuijala and Z. Szabadka, “Brotli Compressed Data Format”, RFC 7932, IETF, 2016.