Shipping ItLesson 9 of 74

Contents

Shipping It

Lesson 9 of 74. Sign in to keep your place.

Module 1 · How the internet reaches your process

The handshake you keep paying for

9 min read

Setting up a connection costs more than using it

Before a single byte of your response moves, a new HTTPS connection costs: one round trip for the TCP handshake, then one or two more for the TLS handshake. On a 40 ms link that is 80 to 120 ms spent agreeing to talk. Your handler might take 5 ms.

This is why connection reuse is one of the largest, cheapest wins available to a small site, and why the same mistake shows up in three different places.

Keep-alive, and the header that turns it off

HTTP/1.1 connections are persistent by default: the browser sends a request, gets a response, and holds the socket open for the next one. Connection: close ends it. Some frameworks, proxies and old tutorials still set that header, and the effect is a fresh handshake per request, which on a page with 30 assets is 30 handshakes.

On the server side there is a timeout for idle keep-alive connections, usually 5 to 75 seconds. Two things about it matter:

  • Each held-open connection occupies a slot. A server configured for 200 connections with a 75-second idle timeout can be full of people who left.
  • If your load balancer’s idle timeout is longer than your app’s, the app will close a connection at the moment the balancer is handing it a request, and you get a small, steady drizzle of 502s that nothing in your code explains. Make the app’s timeout the longer of the two. This is one of the most-reported mystery errors behind a proxy.

HTTP/2 and HTTP/3

HTTP/2 sends many requests down one connection at once, which removes the old six-connections-per-host limit and makes handshake cost almost irrelevant for a browser. In practice you get it by turning it on at your proxy or CDN; it is one line of configuration and free.

It has one real weakness: everything is on one TCP connection, so a single lost packet stalls every stream on it until the retransmission arrives. That is head-of-line blocking, and it is worst on flaky mobile networks, exactly the networks much of this audience is on.

HTTP/3 moves the whole thing to QUIC over UDP, where each stream is independent, so a lost packet affects one stream. It also merges the transport and TLS handshakes into one round trip. On a good fixed line the difference is small; on a congested mobile network it is not.

One caution repeated from the security literature: the 0-RTT resumption mode, which sends data before the handshake completes, is replayable by an attacker. Use it for GETs, never for anything that changes state.

The bug in your own outbound calls

Everything above is about traffic coming in. The version that actually costs small services money is outgoing.

If your code calls a payment provider, a mail API or your own second service with a fresh client per request, you pay TCP plus TLS every time. Under load you also exhaust ephemeral ports and accumulate sockets in TIME_WAIT.

Python
# every call: new connection, new TLS handshake
requests.get(url)

# one connection, reused
session = requests.Session()   # create once, at module scope
session.get(url)

The same fix in other languages: a single http.Client in Go, one HttpClient in .NET, Node’s keepAlive agent (on by default since Node 19, off before it), a connection pool in your HTTP library of choice. Measure it with:

Bash
ss -s                       # totals, including TIME_WAIT
ss -tn state time-wait | wc -l
curl -w '%{time_connect} %{time_appconnect} %{time_total}\n' -o /dev/null -s https://example.com

Run that curl twice in a row against your own API. The second run does not get faster on its own, but comparing time_connect and time_appconnect tells you exactly how much of your latency is handshake, and therefore how much reuse would remove.

Where this shows up as a bill

Some providers charge per connection or per new TLS session; more commonly, handshakes show up as CPU. TLS termination is not free, and on a small instance a few hundred new handshakes a second is measurable. Reuse and a CDN in front are the two ways out, in that order of effort.

What to do with this

  1. Confirm keep-alive is on and no proxy is sending Connection: close.
  2. Make your app’s idle timeout longer than the load balancer’s.
  3. Turn on HTTP/2 at the edge; add HTTP/3 if your CDN offers it, which is usually a checkbox.
  4. Find every place your code makes an outbound HTTP call and give it one shared, pooled client.

The fourth item is where the surprise usually is.

Every lesson here is open. Your progress is recorded once you pass the Agents and Automation exam.

Check your understanding

Your app sits behind a load balancer with a 60-second idle timeout; the app’s own keep-alive timeout is 5 seconds. Users see occasional 502s with nothing in the application log. What is the mechanism?

Pick the one you would defend. Your answer stays private.

Next lesson · 8 minThe key you must not commit

Stuck on something?Ask Tri, your AI tutor

How to cite: Addaly (2026) ‘The handshake you keep paying for’, in Shipping It. Edited by M. Mun. Addaly. Available at: https://addaly.com/learn/cloud-and-shipping/keeping-the-connection-open (Accessed: 13 October 2026).