Networking through 5 layers: what happens when you open viet.sarl

Following one page load from start to finish: DNS, the TCP handshake, TLS 1.3, HTTP/2, IP routing, Ethernet and Wi-Fi frames, down to physical signals. The 5-layer model mapped to the 7 OSI layers, measured with dig, ping, curl and Python on this very website.

The previous four posts stayed inside one machine: bits and addition, the CPU and caches, memory, the operating system. The last one goes outside, following exactly what you just did to read this post: type a website's address and wait for it to appear. On a fresh connection that takes about half a second, and in that half second your request goes through the five layers of computer networking twice: down on your machine, up on the server, then back again.

The route: the 5-layer model and the 7 OSI layers, the whole page load on a timeline, then each layer from the top (application with DNS, TLS, HTTP; transport with TCP; network with IP; data link; physical), how one packet is wrapped, what's behind Cloudflare, and the practical lessons.

Every number in this post was measured on a real machine, against viet.sarl itself, with dig, ping, curl, openssl, traceroute and Python 3.12. Anything about the measuring side's network (IP address, provider, the hops along the way) and the origin server's address has been deliberately left out; only what anyone can see from outside is printed.

Five layers, and the seven OSI layers

Computer networking is split into layers, each doing one job and talking only to the layer directly above and below it. An upper layer doesn't need to know how the lower one works; it hands data down and trusts it'll arrive.

There are two common ways to split it. The OSI model has 7 layers and is the standard one in textbooks and documentation. The 5-layer model folds the top three OSI layers into one, and matches how the Internet actually runs (the TCP/IP protocol suite). This post follows the 5 layers; the table is here so you'll recognize a layer's name wherever you meet it:

5-layer model Matching OSI layers Unit of data In the viet.sarl page load
5. Application 7 Application, 6 Presentation, 5 Session Message DNS, TLS, HTTP/2
4. Transport 4 Transport Segment TCP, port 443
3. Network 3 Network Packet IPv4 or IPv6, routed over 13 hops
2. Data link 2 Data link Frame Wi-Fi or Ethernet, MAC addresses
1. Physical 1 Physical Bit Radio waves, electrical signals, light in fibre

(TLS is often placed in OSI's "presentation" or "session" layer, because it encrypts data and keeps a secure session; in the 5-layer model it sits in the application layer, just above TCP.)

The layers work together like posting a letter in nested envelopes: the application layer writes the letter (an HTTP request), the transport layer puts it in an envelope marked with port numbers, the network layer puts that in an envelope marked with IP addresses, and the link layer puts that in an envelope marked with the MAC address of the next hop. At the other end, each layer opens its own envelope. This nested wrapping is called encapsulation.

The whole picture: how half a second splits up

curl can report when each stage finishes. Opening https://viet.sarl/series five times, each on a fresh connection:

curl -s -o /dev/null -w '%{time_namelookup} %{time_connect} %{time_appconnect} %{time_starttransfer} %{time_total}\n' https://viet.sarl/series
Milestone (from the start) Median of 5 Range
DNS done (IP address known) 2 ms 2–60 ms
TCP handshake done 68 ms 51–122 ms
TLS handshake done 145 ms 123–222 ms
First byte of the page received 587 ms 336–1,956 ms
Whole page received 592 ms 343–1,958 ms

For comparison, a ping packet to Cloudflare's nearest server and back takes 55–99 ms (74 ms on average). That number is the RTT (round-trip time), the most important unit of measure in networking: TCP takes about one RTT, TLS about one more, and the HTTP request at least one more. The rest is the server working. Each milestone in the table is explained in the sections below.

Layer 5, application: DNS

A computer doesn't know where viet.sarl is; it needs an IP address. DNS (Domain Name System) is the Internet's phone book:

$ dig viet.sarl A
viet.sarl.    300    IN    A    172.67.207.225
viet.sarl.    300    IN    A    104.21.22.246
;; Query time: 232 msec

$ dig viet.sarl AAAA
viet.sarl.    300    IN    AAAA    2606:4700:3034::6815:16f6
viet.sarl.    300    IN    AAAA    2606:4700:3036::ac43:cfe1
  • The name maps to two IPv4 addresses (A records) and two IPv6 addresses (AAAA records), all belonging to Cloudflare, not to the website's real server. Cloudflare stands in between, hiding the origin server's address and absorbing attacks.
  • The 300 is the TTL: the answer may be kept for 300 seconds. That's why the first lookup took 232 ms but the later ones in the table above took 2 ms: the operating system remembered the answer.
  • Asking Cloudflare's public DNS server directly (dig @1.1.1.1) took 93 ms.

When nobody has the answer cached, a DNS server has to ask its way down from the top: a root server points to the server for the .sarl suffix, that server points to the domain's own server (Cloudflare again here), and that one has the answer. Each step is a trip across the network, so an uncached DNS lookup can cost hundreds of milliseconds before the real work even starts.

Layer 4, transport: TCP

With the IP address in hand, your machine opens a TCP connection to port 443 (the default port for HTTPS). A port is a number that lets one machine tell its applications apart: on the same IP address, port 443 is the web, port 22 is SSH.

The network underneath promises nothing: packets can be lost, arrive out of order, or arrive twice. TCP turns that into a reliable, in-order stream of bytes:

  • The three-way handshake: your machine sends SYN, the server answers SYN-ACK, your machine sends ACK (and can send data right after). The two sides agree on starting sequence numbers. That's about one RTT: in the table, TCP finishes at 51–122 ms, in line with ping's 55–99 ms RTT.
  • Sequence numbers and acknowledgements: every byte has a sequence number; the receiver reports how far it has got; a packet that isn't acknowledged after a while is sent again.
  • Congestion control: TCP starts sending slowly and speeds up, and slows down when it sees packet loss, so as not to clog the network.

How much data does each TCP segment carry? Ask the operating system directly:

import socket

s = socket.create_connection(("viet.sarl", 443))
print(s.getsockopt(socket.IPPROTO_TCP, socket.TCP_MAXSEG))  # 1348

1,348 bytes per segment (the MSS, maximum segment size). In theory, with 1,500-byte Ethernet frames, it should be 1,500 − 20 (IP) − 20 (TCP) = 1,460. Smaller than that means some device along the way asked for smaller packets, usually because that stretch of the path wraps everything in an extra layer (a tunnel, a VPN, or some kinds of provider connections). Users see nothing; TCP adjusts on its own.

Layer 5, application: TLS 1.3

On top of the TCP connection, before sending anything meaningful, the two sides build an encrypted channel with TLS:

$ openssl s_client -connect viet.sarl:443 -servername viet.sarl -alpn h2,http/1.1
subject=CN=viet.sarl
issuer=C=US, O=Let's Encrypt, CN=YE2
Protocol: TLSv1.3
ALPN protocol: h2

In one handshake, TLS does three things:

  • Proves identity: the server presents a certificate naming viet.sarl, signed by an organization browsers trust (Let's Encrypt here). Without this step, someone in the middle could pretend to be the website.
  • Agrees on keys: the two sides create a shared secret key that no eavesdropper on the line can work out, then encrypt everything after that with it (AES-256-GCM here).
  • Picks the protocol above (ALPN): the two sides agree to speak HTTP/2 (h2).

TLS 1.3 does all of it in one RTT (older versions needed two), matching the table: TLS finishes about 70–110 ms after TCP. The -servername viet.sarl detail is worth noticing too: the website's name is usually sent unencrypted, before the encrypted channel exists (SNI; a newer extension called ECH can encrypt even that), because one Cloudflare IP address serves millions of websites and the server needs to know which certificate to present.

Layer 5, application: HTTP

Only now does the real request happen. Sending an HTTP/1.1 request by hand over the TLS channel in Python, to see that it's just text:

req = b"GET /series HTTP/1.1\r\nHost: viet.sarl\r\nAccept-Encoding: gzip\r\nConnection: close\r\n\r\n"
Bytes
The request 83
The response's headers 815
The page content, gzip-compressed 6,609
The same content, uncompressed 23,295

The whole "letter" is just 83 bytes of text. The page comes back compressed to less than a third: compressing text before sending it is the cheapest way to save bandwidth.

Real browsers use HTTP/2 (chosen in the ALPN step): requests and responses are packed into binary frames, and many requests (the page, CSS, scripts, images) travel in parallel over the same connection. The response headers also include alt-svc: h3=":443": the server announces that it also speaks HTTP/3, which runs on QUIC over UDP instead of TCP and folds the transport and TLS handshakes into one RTT. Next time, the browser can use it.

Why does the first byte arrive so late (587 ms median, once almost 2 seconds)? The response headers hold the answer: cf-cache-status: DYNAMIC. Cloudflare isn't keeping a copy of this page, so every request has to go on from Cloudflare to the origin server, wait for the application to build the page, and come back. The large variation between runs is that leg. Reusing an already open connection for a second and third request skips the TCP and TLS handshakes, and the first byte arrives after about 130 ms instead of more than half a second.

Layer 3, network: IP and routing

Every packet carries a source and a destination IP address. No machine knows the whole route; each router only knows, for a range of destination addresses, which way to pass a packet on. Packets travel router to router like letters from post office to post office. traceroute counts 13 hops from the measuring machine to Cloudflare, the last with an RTT of about 57 ms. (The hops in between belong to the measuring side's home network and provider, so they aren't printed.)

Each packet also has a TTL field (called the hop limit in IPv6): every router decreases it by 1, and when it reaches 0 the packet is dropped and the router reports back to the sender. That's how traceroute works: send packets with TTL = 1, 2, 3… so each hop reveals itself in turn. TTL exists so that a packet lost in a routing loop doesn't circle forever.

There's something interesting about Cloudflare's addresses: they're anycast. The same IP address is announced from hundreds of data centers around the world, and routing automatically takes packets to the nearest one (in network terms). Someone in Hà Nội and someone in Paris type the same address but talk to two different servers.

IPv4 has only about 4.3 billion addresses, not enough for every device. So machines on a home network usually have private addresses (like 192.168.x.x), and your home router translates addresses (NAT) to a single public one as packets go out to the Internet. IPv6, with 128-bit addresses, doesn't need that trick.

An IP address says where a packet needs to go. To make the very first hop, from your machine to the home router, takes one more layer: the data link layer, which moves a frame between two directly connected devices (on the same Wi-Fi network, or the same Ethernet cable).

  • Every network card has a MAC address (48 bits), meaningful only on the local network. A frame sent from your machine carries the router's MAC as its destination, not Cloudflare's; at each hop the old frame is unwrapped and a new one is made with the next hop's MAC, while the IP packet inside travels unchanged.
  • Your machine learns the router's MAC through ARP (for IPv4) or Neighbor Discovery (for IPv6): ask the whole local network "who has this IP address?", and that device answers with its MAC.
  • An Ethernet or Wi-Fi frame carries at most 1,500 bytes of data (the MTU). ifconfig on the measuring machine shows exactly mtu 1500. That's where the 1,460 in the TCP section comes from.
  • This layer also detects errors: every Ethernet frame ends with a 4-byte check (CRC); a damaged frame is dropped, and TCP above will send the data again.

Layer 1, physical: bits into signals

At the very bottom, each bit has to become something physical: a voltage on a copper wire, a Wi-Fi radio wave, or a pulse of light in a fibre. The physical layer decides the maximum speed (how many bits per second) and, more importantly, the latency nothing can beat.

Light in optical fibre travels about 200,000 km per second, about two thirds of its speed in a vacuum. The smallest RTT measured to Cloudflare was 55 ms, about 27 ms each way; in that time, light in fibre covers about 5,500 km. In reality the server is much closer than that: most of the time is queueing and processing in routers and on the wireless hop, not distance. But the physical limit is real: a server on the other side of the Earth, about 20,000 km away, can never have an RTT below about 200 ms, however fast the network. That's why CDNs like Cloudflare put servers everywhere: the only thing that reduces physical latency is being closer.

How one packet is wrapped

Putting it all together, one full segment of the web page (1,348 bytes of payload, per the measured MSS) looks like this as it leaves your machine (with IPv4, typical header sizes):

Part Bytes Layer
Ethernet header (source MAC, destination MAC, type) 14 2
IPv4 header (IP addresses, TTL…) 20 3
TCP header (ports, sequence numbers…, with the timestamp option) 32 4
TLS record header and authentication tag ~22 5
The actual HTTP/2 data the rest 5
Ethernet checksum (CRC) 4 2

About 90 bytes for the managing layers on each roughly 1,400-byte packet: under 7%. But for small packets (a chat message of a few bytes, or a TCP acknowledgement), the "envelopes" can be bigger than the content, much like the system-call cost in the previous post: every send has a fixed price.

Behind Cloudflare

At Cloudflare's server, the packet goes back up all five layers, TLS is unwrapped, and the HTTP request is read. Since the page isn't cached, Cloudflare opens (or reuses) another connection to the origin server. There, the way this site is deployed, Caddy (a reverse proxy, which appeared in the streaming post of the Python series) takes the request and passes it to uvicorn, where a FastAPI async def function builds the HTML page, in exactly the event loop that the first post of the Python series rebuilt by hand with epoll. The answer travels back along that whole path.

Every series on this site meets here: numbers and addition, the CPU and caches, memory, the operating system and system calls, a Python interpreter, an asynchronous server, and five network layers, all to bring a few thousand bytes of text to your screen in about half a second.

Practical lessons

  • The RTT is the most expensive thing. Every handshake is an RTT; a page needing DNS, TCP, TLS and then HTTP is at least three to four RTTs before the first byte. Cutting round trips matters more than adding bandwidth.
  • Reuse connections. A second request on the same connection needs no handshake: 130 ms instead of more than half a second. HTTP/2 runs many requests over one connection; HTTP/3 shortens the handshake too.
  • Be close to your users. You can't beat the speed of light, only shorten the distance: CDNs and caching at the network edge.
  • Cache in the right place. cf-cache-status: DYNAMIC means every page view goes all the way back to the origin server; a page cached at the edge would answer after about one RTT.
  • Compress text. 23 KB becomes 6.6 KB with gzip alone.
  • Small packets are expensive. Gather data into bigger packets, like every other boundary in this series.

Summary

Layer Its job Measured on viet.sarl
5. Application: DNS Domain name into IP address 232 ms uncached, 2 ms cached; TTL 300 s
5. Application: TLS Verify, encrypt, pick the protocol TLS 1.3, a Let's Encrypt certificate, HTTP/2; one RTT
5. Application: HTTP Request and return content An 83-byte request; 23 KB compressed to 6.6 KB
4. Transport: TCP A reliable, in-order stream of bytes A one-RTT handshake; MSS 1,348 bytes
3. Network: IP Get packets to their destination across routers 13 hops; Cloudflare uses anycast
2. Data link Frames between two neighbouring devices MTU 1,500 bytes; the MAC address changes at every hop
1. Physical Bits into signals Smallest RTT 55 ms; light in fibre ~200,000 km/s

Five posts, from one addition in a few hundred transistors to a web page travelling halfway across the Internet. No layer is magic: just abstractions stacked on top of each other, each solving one problem, and every boundary between two layers having a price that, once understood, can be measured, and avoided.