Lesson 4 · Networking

Why the first request to a new host is always the slow one

Tied to Mission: Backend Depth — the networking phase, and the resource-exhaustion latency spike the baseline assessment surfaced.

Open a browser devtools network tab and hit an API host you haven't talked to yet. The first request takes noticeably longer than the ones right after it, even though the server is doing the exact same work each time. Nothing about the request or the response changed. What changed is everything that had to happen before the request could even be sent.

The RTT tax nobody sees in the response body

An RTT (round-trip time) is the time for one message to travel to the other side and its acknowledgment to travel back. Before a single byte of your actual HTTP request goes out over a fresh connection, two setup steps happen, each spending RTTs of their own:

Add it up and a brand-new HTTPS connection can cost multiple round trips of pure setup latency before your GET or POST is even on the wire — on top of the RTT the request and response themselves need. None of that shows up as "server processing time." It's paid entirely in the network layer, invisibly, every time a new connection has to be established.

Reuse turns the tax into a one-time cost

HTTP keep-alive means the TCP connection (and its TLS session) stays open after the first request completes, so it can carry the next request too. A request sent over an already-open connection skips both handshakes entirely — zero extra round trips before it goes out. That's the whole reason the second request to the same host feels instant compared to the first: the first request paid for the pipe, everything after it just uses the pipe.

Why this matters for backend-to-backend calls

The same math applies when your app server calls a downstream API or a database, not just when a browser calls you. A connection pool is a set of already-established connections an app keeps warm and hands out to whichever request needs one next, instead of opening a new connection per call. As long as a pooled connection is available, a backend-to-backend call pays zero extra handshake RTTs — same as browser keep-alive.

Where this turns into an incident

If the pool is sized too small for the concurrency you're actually seeing, or keep-alive is disabled or timing out too aggressively, requests beyond pool capacity have no warm connection to borrow. They open a fresh one instead — paying the TCP+TLS handshake tax again, on every burst. The downstream service's own processing time hasn't changed at all. What changed is that connection setup overhead is now happening on the hot path, exactly the shape of a "latency spike from resource exhaustion": it looks like the dependency got slower, but the actual bottleneck is upstream of it, in how many connections are available to reuse.

Check yourself

A backend service calls a downstream payment API on every request. It's fast under normal traffic. During a burst, p99 latency spikes even though the payment API's own reported processing time is unchanged and the HTTP client pool is a fixed size. Most likely explanation?
Right — once the pool is exhausted, requests beyond it either queue or force a brand-new TCP+TLS handshake, adding round trips a reused connection wouldn't pay. Same resource-exhaustion shape as any other pool limit, just paid as network setup latency instead of a server timing anything itself. Not quite — the question says the downstream's own processing time didn't change, and this is a latency question, not a throughput one. Think about what's different specifically for the requests that couldn't borrow a pooled connection.

Try it for real

Hands-on

Pick any HTTPS host and run curl -w "connect: %{time_connect} appconnect: %{time_appconnect} total: %{time_total}\n" -o /dev/null -s https://example.com twice in a row without -v forcing a new connection each time — then run it again reusing a connection via curl --keepalive-time 60 https://example.com https://example.com (or just open the same page twice in a browser devtools Network tab and compare the timing breakdown for "Connecting" and "TLS" on the first request versus a later one on the same host). Watch time_connect and time_appconnect shrink to near zero once a connection is reused.

Primary source

"High Performance Browser Networking" by Ilya Grigorik, free at hpbn.co — see the "Building Blocks of TCP" and "Transport Layer Security (TLS)" chapters for the handshake mechanics this lesson simplifies, and the HTTP chapters covering persistent connections for how keep-alive and connection reuse work in practice.

Want to go deeper on TLS 1.3's 0-RTT resumption, or how connection pool sizing actually gets tuned for a real service? Ask directly — that's what this curriculum is for.