You're designing the ingress layer for a global API platform: 50M users across five continents, ~70% mobile, packet loss routinely 1–3%; API p99 target < 150ms, of which connection setup (DNS + TCP + TLS) often eats half of mobile time-to-first-byte. A request from Tokyo to a US-West data center already costs ~120ms of physical round-trip time (RTT); if the handshake takes 3 RTTs, that's 360ms before a single byte arrives.
The problem isn't "add servers"—it's that every RTT must be squeezed: how DNS steers a user to the nearest healthy DC, whether the transport is TCP or UDP, how to cut the handshake from 3 RTTs to 0, and whether load balancing happens at layer 4 or layer 7. These four decisions dominate first-packet latency, and in architect interviews they separate "can configure Nginx" from "understands the network."
graph LR
U["Client
mobile/browser"]
DNS["DNS resolve
GeoDNS + Anycast"]
EDGE["Edge / L4 LB
Anycast VIP · DSR"]
L7["L7 LB / Gateway
TLS terminate · route"]
APP["App cluster"]
U -->|"① name→IP"| DNS
U -->|"② TCP/QUIC handshake"| EDGE
EDGE -->|"③ TLS + HTTP"| L7
L7 -->|"④ keep-alive pool"| APP
classDef client fill:#1a2530,stroke:#64c8ff,color:#e8eef5
classDef edge fill:#0e2030,stroke:#5eead4,color:#e8eef5
classDef app fill:#2a1530,stroke:#ff7ab6,color:#e8eef5
class U client
class DNS,EDGE,L7 edge
class APP app
DNS picks the DC; L4 picks the edge box; L7 terminates TLS and routes by path/tenant; origin reuses long-lived connections to skip handshakes
Each component owns a slice of latency: DNS steers users nearby (Anycast lets BGP decide "nearest"); L4 LB forwards by the connection 4-tuple—very high throughput but blind to content; the L7 LB/gateway terminates TLS and, seeing the HTTP message, does path routing, rate limiting, canary; the origin uses a keep-alive pool to amortize handshakes. Understanding this chain is how you locate "which RTT is slow."
Principle: TCP provides a reliable, ordered, connection-oriented byte stream—three-way handshake to connect, sequence numbers + ACK retransmit for loss, sliding window for flow control, congestion control (Cubic/BBR) to avoid overwhelming the network. The cost: 1 RTT to connect, and "ordered" delivery is a double-edged sword—if one packet is lost, already-arrived bytes must wait in the kernel buffer for the retransmit. That is the root of head-of-line (HOL) blocking. UDP is connectionless datagrams: fire-and-forget, no ordering, no congestion control—zero handshake, zero HOL blocking, but reliability/ordering/congestion are all the application's problem.
# Selection intuition: first ask "if one packet is lost, can the app tolerate it?"
lossy-OK, want latest → UDP (voice: a dropped frame beats a stale one)
lossless, need ordering → TCP / QUIC (API, files, payments)
ordered but fear HOL blocking + frequent mobile network switch → QUIC (HTTP/3)
Principle: HTTP's performance history is essentially three wars against HOL blocking. HTTP/1.1 processes one request at a time per connection (pipelining was practically abandoned), so browsers open 6 parallel TCP connections—connection-level HOL blocking. HTTP/2 does application-layer multiplexing over one TCP connection: requests are sliced into frames tagged with stream IDs and interleaved, solving application-layer HOL blocking—but all streams still share one TCP byte stream, so a single lost TCP packet stalls every concurrent stream (transport-layer HOL blocking remains, and hurts more than HTTP/1.1 because the losses of 6 connections are compressed into 1). HTTP/3 drops TCP entirely and runs over QUIC: each stream has its own sequence space, so loss only stalls its own stream while the rest keep flowing—HOL blocking is fully solved.
| HTTP/1.1 | HTTP/2 | HTTP/3 | |
|---|---|---|---|
| Transport | TCP | TCP | QUIC / UDP |
| Concurrency | multiple conns | single-conn mux | single-conn mux |
| HOL blocking | connection-level (severe) | TCP transport still | none (per-stream) |
| Setup RTT | TCP+TLS 2–3 | TCP+TLS 2–3 | 1 (incl. TLS) / 0-RTT |
| Headers | plaintext, repeated | HPACK compressed | QPACK compressed |
| Network switch | breaks | breaks | connection migration |
Key insight: on high-loss mobile/transoceanic links, HTTP/2 may underperform HTTP/1.1's multiple connections—putting all eggs in one TCP basket means one jolt spills them all. That's exactly why you must descend to QUIC: HOL blocking lives in the transport layer, so bumping the HTTP version alone can't cure it—you have to change the transport.
Principle: TLS layers encryption over plaintext TCP. TLS 1.2 needs 2 RTTs (ClientHello→ServerHello+cert→key exchange→Finished) before the first byte; stacked on TCP's 1 RTT that's 3 RTTs. TLS 1.3 cuts a negotiation round to just 1 RTT; for returning users a session ticket enables 0-RTT: the client puts application data (early data) in the very first packet, running handshake and data in parallel. QUIC goes further, merging the transport and TLS handshakes—1-RTT for new connections, 0-RTT for returning ones. Each saved RTT on a transoceanic link is tens to hundreds of milliseconds.
sequenceDiagram
participant C as Client
participant S as Server
Note over C,S: TLS 1.2 (2-RTT)
C->>S: ClientHello
S->>C: ServerHello + Cert
C->>S: KeyExchange + Finished
S->>C: Finished ✓ only now can send data
Note over C,S: TLS 1.3 0-RTT (resumption)
C->>S: ClientHello + early data (request already here)
S->>C: response + Finished
POST /transfer—otherwise one transfer could be replayed into two. In practice: browsers only send GET in 0-RTT; servers force a full 1-RTT handshake for non-idempotent requests.
Principle: before a request goes out it needs DNS resolution—a recursive resolver walks root→TLD→authoritative servers, relying on TTL caching to avoid the full walk each time. Going global uses two tricks: GeoDNS returns different IPs by resolver geography; Anycast announces the same IP from many locations, letting BGP route each user to the network-nearest node. Once traffic reaches a DC, load balancing splits into two layers: L4 (transport) only sees the IP:Port 4-tuple and forwards by connection—no decryption, extreme throughput, often with DSR (return path bypasses the LB); L7 (application) terminates TLS and parses HTTP, so it can route by URL path/header/cookie, do canary, rate limiting, WAF—at the cost of decrypting and parsing every connection.
| L4 (transport) | L7 (application) | |
|---|---|---|
| Sees | IP:Port 4-tuple | full HTTP (URL/header/cookie) |
| Can do | connection forwarding, DSR | path routing, canary, rate limit, TLS terminate |
| Throughput/cost | very high / low | lower / high (decrypt+parse) |
| Typical | LVS, AWS NLB, Maglev | Nginx, Envoy, AWS ALB |
# Common production combo: Anycast → L4 (volume+balance) → L7 (smart routing)
Client --DNS/Anycast--> [L4 LB: consistent hash picks edge, DSR return]
--> [L7 gateway: terminate TLS, /v2/* canary 5%]
--> App pool (keep-alive reuse, no re-handshake)
# L4 uses consistent hashing so existing connections aren't reshuffled when backends change (see Day 4)
Frequent follow-ups:
HTTP/2 cold start: DNS (≈0 on local cache hit, else 1 RTT) + TCP three-way handshake 1 RTT + TLS 1.3 handshake 1 RTT before the first request goes out, then 1 RTT to fetch the response. Excluding DNS ≈ 3 RTTs ≈ 360ms; add ~1 RTT if DNS misses.
HTTP/3 first time: QUIC merges transport and TLS handshakes into 1 RTT, plus 1 RTT for the response ≈ 2 RTTs ≈ 240ms—one whole RTT saved.
HTTP/3 returning 0-RTT: the request rides the first packet (early data), response returns 1 RTT later ≈ 120ms—from 360ms to 120ms, the difference between "spinning" and "instant" on a transoceanic link.
This is why big players fight over handshake RTTs: the speed of light is non-negotiable; the only thing you can cut is the number of round trips.
In the HTTP/1.1 era browsers opened 6 TCP connections; one stalling left the other 5 running—loss was diffused. HTTP/2 compresses all requests into one TCP connection; the application appears concurrent, but once the underlying byte stream drops a packet, TCP must deliver in order, so all streams wait for the retransmit together. On low-loss links multiplexing wins net; on high-loss links, one basket for all eggs is more fragile.
Deciding: look at your users' network quality. Intra-DC/low-loss → HTTP/2's gain is clear; heavy mobile-weak/transoceanic → either keep multiple connections or go straight to HTTP/3 (QUIC's per-stream independence is immune at the root). Don't chase "higher version number" alone.
Risk: Anycast's "nearest" is decided by BGP routing, which flaps with network conditions. If a long-lived TCP connection has its subsequent packets rerouted mid-flight by BGP convergence to another node, that node holds no state for the connection (seq, TLS session), so the connection is RST-reset. Traditionally Anycast suits short, stateless exchanges (DNS's one-question-one-answer).
Why QUIC fits: QUIC identifies a connection by a connection ID rather than the 4-tuple, and supports connection migration—even if packets land on a new node, or the client changes IP/port, the connection ID lets it reclaim the connection without re-handshaking. This makes Anycast + long-lived connections truly viable for the first time, and is also why mobile devices survive a WiFi/cellular switch without dropping.
Cost of terminating TLS: every connection is decrypted at L7, high CPU cost, per-box throughput far below L4; and plaintext appears in the LB's memory, becoming an attack surface (keys, request bodies concentrated). The gain is routing by URL/header, canary, rate limiting, WAF—impossible without decryption.
Why two layers: L4 carries volume, L7 carries intelligence. L4 (Maglev/LVS/NLB) uses consistent hashing to spread massive connections evenly across a fleet of L7s, itself never decrypting, with extreme throughput, and can run DSR so return packets bypass the LB straight to the client (saving half the bandwidth); each L7 only handles its share for fine-grained routing. Pure L7 at the very front is both expensive and hard to scale horizontally, and pipes DDoS straight into the decryption layer. Layering means "coarse-filter first, fine-tune second."
The through-line: the speed of light fixes a single RTT; architecture can only "reduce the number of round trips" and "amortize them," and every aggressive RTT saving (0-RTT/TFO) trades security or consistency for latency.