system-design

Networking 101: The Journey of a Request

The single most common interview question in tech — “what happens when you type google.com?” — answered properly, one layer at a time.

Prerequisites: Computer Fundamentals Time to read: ~20 minutes


The problem

You write fetch("https://api.example.com/users") and data comes back. Between those two moments, roughly a dozen systems cooperated across thousands of kilometres, any of which can fail, be slow, or lie to you.

You cannot design distributed systems without a mental map of that journey, because every box you draw in an architecture diagram is an arrow that crosses a network, and every one of those arrows can be slow, drop, duplicate, or reorder your data.


🧠 Mental model: the postal system

Sending a letter from Lahore to Tokyo:

Postal Network
The letter you wrote Your HTTP request (application data)
The envelope with an address TCP segment (ports = which mailbox at that address)
The address on the envelope IP packet (source and destination IP)
Trucks, planes, sorting centres Routers, switches, cables
The postal worker’s route decisions Routing tables, BGP
Registered mail with delivery confirmation TCP (guaranteed, ordered, retried)
Dropping a postcard in a box and hoping UDP (fast, no guarantees)
Looking up the address in a directory DNS
Sealing it in a tamper-proof pouch TLS

Notice: the post office doesn’t read your letter. Routers don’t understand HTTP. Each layer only knows about its own envelope. That’s the whole idea of layering, and it’s why you can invent a new application protocol tomorrow without asking anyone to upgrade the internet.


The layers

The textbook OSI model has 7 layers. In practice, engineers use 4–5 and it looks like this:

flowchart TB
    A["Application — HTTP, gRPC, DNS, SMTP<br/>'GET /users HTTP/1.1'"]
    B["Transport — TCP, UDP, QUIC<br/>ports, reliability, ordering"]
    C["Network — IP<br/>addressing and routing between networks"]
    D["Link — Ethernet, Wi-Fi<br/>getting a frame to the next hop"]
    A --> B --> C --> D
    D --> E[("The wire / the air")]

Each layer wraps the one above it. Your 20-byte JSON body becomes:

[ Ethernet header ][ IP header ][ TCP header ][ HTTP headers ][ {"id":42} ][ Ethernet trailer ]
       14 bytes        20 bytes     20 bytes     ~200–800 bytes    20 bytes

📐 Note the overhead: a 20-byte payload can travel inside a 900-byte packet. This is why chatty protocols are expensive, why HTTP/2 compresses headers, and why binary protocols like gRPC win for internal high-volume traffic.

What you need to remember about each layer:

Layer Unit Addresses by Your design concern
Application message URL / service name API design, payload size
Transport (L4) segment port TCP vs UDP; L4 load balancing
Network (L3) packet IP address Routing, subnets, region placement
Link (L2) frame MAC address Almost never (unless you do networking for a living)

🚨 “L4 vs L7 load balancer” is a common interview term and it just means which layer it makes decisions at. L4 sees IPs and ports and forwards blindly (fast). L7 parses HTTP and can route on URL path, cookies, or headers (flexible, slower). → Load Balancers


The full journey: https://api.example.com/users

Step 0 — Is it cached?

The browser checks its own HTTP cache. A valid cached response means zero network. Free is the fastest possible request. → Caching

Step 1 — DNS: turn the name into an IP

api.example.com means nothing to the network. It needs 93.184.216.34.

The OS checks its cache, then asks a recursive resolver (your ISP’s, or 8.8.8.8, or 1.1.1.1), which walks the hierarchy: root servers → .com TLD servers → example.com’s authoritative nameservers → an answer, cached for the TTL.

📐 A cold DNS lookup costs 20–120 ms. A cached one costs ~0 ms. DNS is also your first, crudest tool for load balancing and failover — and TTLs mean changes propagate slowly, which is why DNS failover is measured in minutes, not seconds. → DNS

Step 2 — TCP: establish a connection

Before a byte of HTTP moves, TCP does a three-way handshake:

Client                          Server
  │  ── SYN (seq=x) ──────────────►│    "can we talk?"
  │  ◄──── SYN-ACK (seq=y,ack=x+1) │    "yes, can we talk?"
  │  ── ACK (ack=y+1) ────────────►│    "yes"
  │                                │
  │  ── HTTP GET /users ──────────►│    finally

That’s one full round trip before any data. Karachi→Virginia: 230 ms spent on saying hello.

This is why connection reuse matters enormously. HTTP keep-alive, connection pools, and HTTP/2 multiplexing all exist to avoid paying this repeatedly. → TCP, UDP, IP

Step 3 — TLS: secure the connection

For HTTPS, another handshake on top: agree on a cipher, verify the server’s certificate against a trusted CA, exchange keys.

📐 So a cold HTTPS request to a distant server costs: DNS (1 RTT) + TCP (1 RTT) + TLS (1–2 RTT) + the request itself (1 RTT) = 4–5 round trips before you see a byte. At 230 ms per RTT, that’s over a second of pure protocol overhead.

This arithmetic — not your code — is why CDNs, keep-alive, and HTTP/3 exist. → HTTP, HTTPS, TLS

Step 4 — Routing: how the packet crosses the world

Your packet doesn’t know the route. It goes to your default gateway (your router), which asks its upstream, and so on. Each router looks at the destination IP, consults a routing table, and forwards it one hop closer. Typically 10–30 hops.

Between networks (ISPs, cloud providers), BGP decides the paths. BGP is based on trust and announcements, which is why BGP misconfigurations occasionally take large chunks of the internet offline for an afternoon.

Practical consequence: you do not control the route, and it changes. Latency between two fixed points varies over time. Design for variance, not for the average. Run traceroute google.com to watch this happen.

Step 5 — The server accepts and responds

The packet reaches the destination machine’s NIC. The kernel looks at the port (443), finds the process listening on it, and hands it the data. Your application code finally runs, produces a response, and the whole journey reverses.

The whole thing

sequenceDiagram
    participant B as Browser
    participant R as DNS Resolver
    participant C as CDN / Edge
    participant L as Load Balancer
    participant S as App Server
    participant D as Database

    B->>R: api.example.com?
    R-->>B: 93.184.216.34 (20–120 ms, or 0 if cached)
    B->>C: TCP handshake (1 RTT)
    B->>C: TLS handshake (1–2 RTT)
    B->>C: GET /users
    Note over C: cache miss — forward to origin
    C->>L: GET /users
    L->>S: GET /users (picks a healthy server)
    S->>D: SELECT … (0.5 ms in-datacenter)
    D-->>S: rows
    S-->>L: 200 OK + JSON
    L-->>C: 200 OK
    C-->>B: 200 OK (and maybe caches it)

Things that will bite you

Packets get lost, duplicated, and reordered. TCP hides this from you — but hiding it costs retransmissions and delay. A 1% packet loss rate can cut TCP throughput by an order of magnitude. This is why “the network is fine, ping works” is not a diagnosis.

MTU and fragmentation. The largest frame most networks carry is ~1500 bytes. Bigger messages are split. If a middlebox blocks the ICMP messages that negotiate this, you get the classic bug where small requests work and large ones hang forever.

NAT and private IPs. Your laptop has a private IP (192.168.x.x); a router rewrites it to a public one. Consequences you’ll actually meet: inbound connections don’t work without port forwarding, NAT tables expire (idle connections die silently after ~5 minutes — hence TCP keep-alives), and inside a VPC everything has private IPs.

Firewalls and security groups. In the cloud, a service being “down” is very often just a security group that doesn’t allow the port. Check this before you debug anything clever.

Bandwidth vs latency are independent. A satellite link can have huge bandwidth and 600 ms latency. Bandwidth is the width of the pipe; latency is its length. More bandwidth does not make a small request faster.


⚖️ Trade-offs

Choice Gain Cost
Keep-alive / connection pooling Skip handshakes (~1–3 RTT saved per request) Idle connections hold server resources
More round trips (chatty API) Simpler, more granular endpoints Latency multiplies, brutally, across regions
Fewer, larger responses Fewer RTTs Wasted bandwidth for clients that need a subset
TLS everywhere Security, integrity, privacy CPU cost + handshake latency (mostly solved by TLS 1.3 + resumption)
UDP instead of TCP No handshake, no head-of-line blocking You must handle loss and ordering yourself

In the real world


🚨 Interview traps


🎙️ Soundbites


🛠️ Try it

Run these and read every line of the output:

dig +trace google.com          # watch the full DNS hierarchy resolve
traceroute google.com          # every router hop, with latency
curl -v https://example.com    # see the TLS handshake and HTTP headers
curl -w "@-" -o /dev/null -s https://example.com <<'EOF'
    dns:        %{time_namelookup}s
    tcp:        %{time_connect}s
    tls:        %{time_appconnect}s
    firstbyte:  %{time_starttransfer}s
    total:      %{time_total}s
EOF

That last one gives you the exact breakdown of where a request’s time went. Run it against a local service and a distant one and compare. Then run it twice in a row against the same host and watch DNS drop to zero.


Check yourself

1. Why does the first request to a site take much longer than the second? The first pays for DNS resolution, TCP handshake, and TLS handshake — 3–4 round trips. The second reuses the cached DNS entry and (with keep-alive) the existing TCP+TLS connection, so it's a single round trip. On a distant server this is the difference between ~1 s and ~250 ms.
2. What's the difference between an L4 and an L7 load balancer, and when would you pick each? L4 works at the transport layer — it sees IP and port, forwards packets/connections without reading content. Very fast, protocol-agnostic, can handle any TCP traffic. L7 parses HTTP, so it can route on path/host/header/cookie, terminate TLS, retry failed requests, and do per-request load balancing. Pick L4 for raw throughput or non-HTTP protocols; L7 when you need content-based routing (which is most web systems).
3. Your service is reachable from your laptop but not from another service in the same VPC. What do you check first? Security groups / firewall rules and the listening address. A very common cause is the process bound to `127.0.0.1` instead of `0.0.0.0` — reachable locally, invisible to everything else. After that: security group ingress rules, subnet routing, and DNS/service discovery resolving to the wrong address.
4. Why is a 1% packet loss rate so much worse than it sounds? TCP treats loss as a congestion signal and dramatically reduces its sending window, then ramps back up slowly. Add the retransmission timeout waits, and throughput can drop by 10× or more from 1% loss. It also inflates tail latency badly — most requests are fine, some are catastrophically slow, so your p50 looks healthy while p99 is terrible.
5. Two datacenters are 5,000 km apart. What is the absolute best possible round-trip latency, and why can't you beat it? Light in fibre travels ~200,000 km/s. Round trip is 10,000 km, so ~50 ms minimum — and real routes aren't straight lines, so 60–80 ms is realistic. This is physics; no protocol, hardware, or vendor fixes it. The only remedies are to make fewer round trips or to move data closer to users.

Further reading