You type example.com into a browser and press Enter. A few dozen milliseconds later, a page is on the screen. In between, your computer has asked a chain of servers for an address, opened a connection to a machine it has never met, agreed on encryption keys with it, checked its identity, asked for a page, received it, and turned a few hundred bytes of text into pixels.
This chapter walks through those steps with real measurements. The network numbers come from curl, which reports how long each phase took, run five times from the Mac this chapter was written on against https://example.com:
$ curl -s -o /dev/null -w '%{time_namelookup} %{time_connect} %{time_appconnect} %{time_starttransfer} %{time_total}\n' https://example.com
0.019641 0.026355 0.045972 0.057660 0.057712 ← first run
0.002037 0.007651 0.020672 0.035067 0.035132
0.001954 0.007644 0.019848 0.035813 0.035875
0.002150 0.008742 0.022763 0.034659 0.034760
0.002337 0.008580 0.021091 0.032697 0.032794
Each column is a moment, in seconds since the start: name resolved, connection open, encryption ready, first byte of the page received, done. Subtracting neighbors gives the cost of each phase:
| Phase | First run | Next four runs |
|---|---|---|
| DNS lookup | 19.6 ms | 2.0–2.3 ms |
| TCP handshake | 6.7 ms | 5.6–6.6 ms |
| TLS handshake | 19.6 ms | 12.2–14.0 ms |
| request → first byte of the answer | 11.7 ms | 11.6–16.0 ms |
| total | 57.7 ms | 32.8–35.9 ms |
The animation below follows the same five phases. The bar at the bottom is drawn to scale: switch between the first visit and the next ones to see where caching saves time and where it can't.
Try it: Press Play to follow the action down through the levels, or use the arrows and the dots to go at your own pace.
- 0Apps
- 1Algorithms
- 2C
- 3Assembly
- 4OS
- 5Machine code
- 6µops & buses
- 7Logic gates
- 8Transistors
- 9Physics
You press Enter
The browser has a name, example.com, but computers on the internet are reached by number. First it needs the address.
1. DNS: from a name to an address
Computers on the internet are reached by IP addresses, not names. The Domain Name System (DNS) is the distributed directory that maps one to the other. Your computer asks a resolver (usually run by your internet provider, your router, or a public service), and the resolver does the legwork, asking servers down the hierarchy of the name from right to left:
- a root server knows who's responsible for
.com; - a
.comserver knows who's responsible forexample.com; - that domain's authoritative server knows the address.
We can play resolver by hand with dig, asking each level without letting it recurse:
$ dig @198.41.0.4 example.com +norecurse ← a.root-servers.net, 18 ms
com. 172800 IN NS l.gtld-servers.net. …and 12 others
$ dig @192.5.6.30 example.com +norecurse ← a.gtld-servers.net, 7 ms
example.com. 172800 IN NS hera.ns.cloudflare.com.
example.com. 172800 IN NS elliott.ns.cloudflare.com.
$ dig @hera.ns.cloudflare.com example.com +norecurse ← 7 ms
example.com. 300 IN A 104.20.23.154
example.com. 300 IN A 172.66.147.243
The number before IN is the TTL, how many seconds an answer may be cached: two days for the list of .com servers, five minutes for the address. Caching is what makes DNS fast. The first curl run paid 19.6 ms for its lookup; the next four found the answer already cached and paid about 2 ms. The domain also has two IPv6 addresses, and curl used one of them, 2606:4700:10::6814:179a.
A DNS query is tiny. It usually travels in a single UDP packet to port 53, and the question itself is 29 bytes: a 12-byte header, the name encoded as length-prefixed labels (7 example 3 com 0), and two 16-bit numbers for the record type (A, an IPv4 address) and class (IN, internet). The reply dig received is 61 bytes: the header, the question echoed back, and the two addresses. Each answer in it begins with c0 0c, a compression pointer: instead of repeating example.com, it points back to offset 12, where the name first appeared.
2. TCP: opening a connection
With an address in hand, the browser opens a TCP connection, a reliable, ordered stream of bytes over a network that loses, duplicates and reorders packets. Opening it takes the three-way handshake: the client sends a SYN, the server answers SYN-ACK, the client sends ACK. The client can send data right after its ACK, so the handshake costs one round trip: 5.6 to 6.7 ms here, which is also about how long any packet takes to reach this server and come back.
For the program, all of this is a handful of system calls: socket creates an endpoint, connect starts the handshake and blocks until it completes, and later write and read move bytes. Everything below (splitting the stream into packets, numbering them, retransmitting lost ones, adjusting the sending rate to the network's capacity) happens in the kernel's TCP/IP stack, as described in the system calls chapter.
Below the kernel, the network interface does the physical work. The driver leaves packets in memory and the network card fetches them itself by DMA (direct memory access), without the CPU copying anything; when packets arrive, the card writes them into memory and raises an interrupt, as described in the traps and interrupts chapter. How the bits cross the wire or the air is the subject of the modems and Ethernet chapter.
The classic picture of this path is the same in spirit: the browser hands an HTTP request to TCP, which adds a header and hands it to IP, which adds another, down to the link layer and its checksum. A decade ago, home users reached the internet over ADSL on a telephone line, and 40-gigabit Ethernet was still on its way. Today home access is mostly fiber, cable or mobile, and data centers run Ethernet at 400 and 800 Gbit/s.
3. TLS: a private, authenticated channel
The https in the address means the connection must be encrypted and the server must prove who it is. That's the job of TLS (Transport Layer Security). OpenSSL's test client shows what was agreed with this server:
$ openssl s_client -connect example.com:443 -brief
Protocol version: TLSv1.3
Ciphersuite: TLS_AES_256_GCM_SHA384
Peer certificate: CN=example.com
Signature type: ecdsa_secp256r1_sha256
Negotiated TLS1.3 group: X25519MLKEM768
- TLS 1.3 needs one round trip for its handshake. In the table, the handshake took 12–14 ms: that round trip plus the computation on both sides: generating and combining keys, and checking the server's signature and certificates.
- X25519MLKEM768 is the key exchange: the two sides agree on a shared secret that nobody watching the traffic can compute. It's a hybrid of a classic elliptic-curve method (X25519) and ML-KEM, a newer one designed to resist future quantum computers, a change that became the default in browsers and servers only recently.
- The certificate says "this public key belongs to
example.com", signed by a certificate authority, whose own certificate is signed by another, up to a root that your operating system trusts. This one's chain has four certificates, and the site's own certificate is valid for 90 days. - AES-256-GCM is the cipher that encrypts every byte after the handshake.
AES is where the lowest levels show. Modern CPUs have instructions that perform one round of AES encryption on 16 bytes in a single step: AES-NI on x86, the cryptography extensions on ARM. OpenSSL's benchmark on one core of this Mac shows what they're worth:
| AES-256-GCM on 16 KB blocks | Throughput |
|---|---|
| with the ARM AES instructions | 7.4–7.5 GB/s |
with them disabled (OPENSSL_armcap=0) | 168–169 MB/s |
A factor of 44. With the dedicated instructions, encrypting a web page costs almost nothing; without them, a fast network connection would keep a core busy. The principle that hardware and software are logically equivalent is visible here: the same algorithm, in software or in silicon, differing only in speed. The system curl on this Mac, built on a different TLS library, negotiated ChaCha20-Poly1305 instead, a cipher designed to be fast using only ordinary additions, XORs and rotations; OpenSSL runs it at about 1.9 GB/s on the same core.
4. HTTP: the request and the answer
Only now does the browser ask for the page. Over the encrypted channel it sends an HTTP request, and curl -v shows both sides (response headers trimmed):
> GET / HTTP/2
> Host: example.com
> User-Agent: curl/8.7.1
> Accept: */*
< HTTP/2 200
< content-type: text/html; charset=utf-8
< server: cloudflare
< age: 9217
< cf-cache-status: HIT
200 means success. The protocol is HTTP/2, agreed during the TLS handshake: it sends headers in a compact binary form and can carry many requests at once over one connection. The newest version, HTTP/3, runs over QUIC on UDP, merging the transport and TLS handshakes to save a round trip.
The headers also reveal that the machine answering isn't "the" server of example.com at all. cf-cache-status: HIT and age: 9217 say a CDN (content delivery network) server near this Mac served a copy it had kept for about two and a half hours. Most popular sites work this way: the page comes from a cache a few milliseconds away rather than from a distant origin. The 11.6–16 ms before the first byte is one round trip for the request plus the server's time to find the answer.
The body is 713 bytes of HTML: a title, a short style sheet, one paragraph, a link, and a <script> tag pointing to a small script, /s.js, which the browser will have to fetch with a second request.
5. From bytes to pixels
Now the browser's own work starts. In Chrome, each site runs in its own renderer process, sandboxed from the rest of the system; one Chrome instance on this Mac was running as eight processes: the browser itself, a GPU process, a network service, a storage service and four renderers. The renderer:
- parses the HTML into a tree of elements, the DOM (Document Object Model), and the CSS into style rules;
- runs the page's JavaScript, which can change both (here, the one small script);
- computes each element's style, then does layout: the position and size of every box, and the line breaks in every paragraph, which needs the text shaping described in the keypress chapter;
- paints: turns the boxes, borders and text into drawing commands;
- hands the result to the compositor, which rasterizes it, often on the GPU, and puts it on screen at the next display refresh.
For a real page, this step repeats as the page's other resources arrive: each image, style sheet, script and font is another request, often to other domains, each with its own DNS lookup, connection and TLS handshake. A typical news or shopping page makes dozens to hundreds of requests. That's why browsers keep connections open for reuse, cache aggressively, and start requests for resources they expect before the page asks for them.
Where the levels show up
| Step | What carries it out |
|---|---|
socket, connect, write, read | system calls into the kernel's TCP/IP stack |
| packets in and out | DMA by the network card, then an interrupt |
| waiting for the network | the process is blocked; the scheduler runs something else |
| AES-GCM encryption | dedicated AES instructions in the ISA |
| key exchange and signatures | big-number arithmetic, compiled from C and assembly |
| HTML parsing and layout | ordinary code, whose speed depends on caches and branch prediction |
| the renderer | a separate, sandboxed process |
| paint and composite | the GPU and the display's refresh |
Most of the 33 ms is waiting: three round trips of about 6 ms to the server (for TCP, TLS and HTTP), plus the DNS lookup, which costs several more when the answer isn't cached. During that time the CPU does almost nothing for this page. The speed of light in fiber is about 200,000 km/s, two thirds of its speed in a vacuum, so every 1,000 km between you and a server adds at least 10 ms to each round trip. No faster processor fixes that; the answer is to need fewer round trips (TLS 1.3, HTTP/3, reused connections) and to put the server closer (CDNs).
Takeaways
- Loading a page is DNS (name to address), TCP (a reliable connection), TLS (encryption and identity), HTTP (request and response), then parse, layout, paint in the browser.
- Measured against
example.com: about 2 ms of cached DNS (20 ms uncached), 6 ms per round trip, 12–14 ms of TLS, about 33 ms in all. - Caching is everywhere: DNS answers carry a TTL, and a CDN served this page from a copy 2.5 hours old.
- TLS 1.3 here used a post-quantum hybrid key exchange and AES-256-GCM, which the CPU's AES instructions run 44 times faster than plain code.
- For the program, networking is a few system calls; the kernel's TCP/IP stack, DMA and interrupts do the rest.
- Most of the time is spent waiting for round trips, so the big wins come from fewer round trips and closer servers, not faster CPUs.