skip to content

What does a client actually pay, in round trips and CPU, to open a brand-new HTTPS connection, and why does that cost justify engineering effort to reuse connections?

level: middleimportance: must knowfreq 55%

answer

  1. DNS + 1 RTT TCP + 1-2 RTT TLS before the request
  2. TLS 1.3 = 1 RTT full, resumption cheaper, 0-RTT replay-unsafe
  3. Asymmetric crypto CPU per handshake
  4. Cold connection = small congestion window, slow start
  5. Churn burns ephemeral ports / TIME_WAIT / LB state

basics

~20 s

A new HTTPS connection costs a DNS lookup, a TCP three-way handshake (1 RTT), and a TLS handshake (1 RTT with TLS 1.3, 2 with TLS 1.2), plus asymmetric-crypto CPU on both ends and a cold TCP congestion window. Reuse skips all of it.

solid answer

~50 s

Setup cost, before a single request byte moves: 1. **DNS** - possibly a network lookup if not cached. 2. **TCP handshake** - SYN, SYN-ACK, ACK: one full round trip. 3. **TLS handshake** - one RTT on TLS 1.3, two on TLS 1.2 (session resumption or 0-RTT can cut this). So over a 50 ms link you burn roughly 100-150 ms before the request is even sent. On top of that: the server does an asymmetric signature per full handshake, which is orders of magnitude more expensive than the symmetric crypto used afterwards; a fresh connection starts with a small congestion window (~10 segments) and has to slow-start up to bandwidth; and each connection consumes an ephemeral port, a socket, and conntrack/load-balancer state, so churn can exhaust ports or fill TIME_WAIT. Reusing a warm connection turns all of that into zero round trips and a socket that has already slow-started. That is why connection pooling is the single cheapest latency win in most HTTP clients.

code

bash · 1 line
bash
curl -o /dev/null -s -w 'dns=%{time_namelookup} tcp=%{time_connect} tls=%{time_appconnect} ttfb=%{time_starttransfer} total=%{time_total}\n' https://api.example.com/health

go deeper

for a junior

Be able to list the steps - DNS, TCP handshake, TLS handshake - and say that reuse skips them, so pooling makes repeat calls faster.

for a middle

Quantify it: RTT counts per phase, TLS 1.3 versus 1.2, plus server CPU for asymmetric crypto and the cold congestion window.

for a senior

Connect it to production symptoms: port exhaustion, TIME_WAIT, load-balancer new-connections-per-second limits, and handshake CPU as a DoS surface.

for a principal

Reason about it as a fleet-level budget - connection churn versus idle connection memory, when to move to HTTP/2 or HTTP/3, and how connection pinning interferes with scale-out and failover.

## The anatomy of a cold request When a client issues its first HTTPS request to a host, the sequence is: 1. **Name resolution.** If the hostname is not in the OS or process cache, a DNS query goes out - often 1-50 ms, more if a recursive resolver has to walk the chain. 2. **TCP three-way handshake.** SYN -> SYN-ACK -> ACK. The client can send data with its final ACK, so this costs **one round-trip time (RTT)**. 3. **TLS handshake.** In TLS 1.3 the client sends its key share with ClientHello and the server responds with everything needed, so a full handshake is **1 RTT**. TLS 1.2 needs **2 RTT** (ClientHello/ServerHello, then key exchange and Finished). Session resumption via a ticket is 1 RTT; TLS 1.3 0-RTT early data can be zero but is replay-unsafe and must only carry idempotent requests. 4. Only now does the HTTP request go out, and the response takes another RTT. On a 50 ms path, a TLS 1.3 connection therefore spends ~100 ms on setup plus ~50 ms for the exchange - roughly a 3x latency penalty against a request on a warm connection. ## The costs that are not round trips **CPU.** A full TLS handshake requires asymmetric operations - an ECDSA or RSA signature by the server, key agreement on both sides. RSA-2048 signing is dramatically more expensive than the AES-GCM that protects the rest of the session. A server facing high connection churn can burn most of its CPU on handshakes while transferring almost no data; this is also a cheap denial-of-service vector. **Congestion window.** TCP does not know the path capacity, so a new connection begins in slow start with an initial window of about 10 segments (~14 KB). A 200 KB response on a fresh connection needs several RTTs of window growth; on a warm connection the window is already large. This is why cold connections are slow for *big* responses too, not just small ones. **Kernel and middlebox state.** Each connection uses a socket, file descriptor, and an ephemeral source port. A closed connection lingers in TIME_WAIT (commonly 60 s) on the side that closed first. A client making thousands of short-lived connections per second to one destination can exhaust its ~28k ephemeral ports and start failing with "cannot assign requested address". NAT gateways, stateful firewalls, and load balancers hold per-connection state too, and their connection tables and new-connection rates are finite - many managed load balancers bill or throttle on new connections per second. ## What reuse buys A request on an already-open pooled connection pays zero setup round trips, zero asymmetric crypto, and rides an already-grown congestion window. For chatty service-to-service traffic this routinely halves p99 latency and removes a whole class of port-exhaustion incidents. It is also why HTTP/2 and HTTP/3 concentrate all traffic to an origin onto one connection: the expensive thing is the connection, not the request. ## Where reuse does not help - **Very low request rates.** If you talk to a host once an hour, the connection will have been idled out anyway; keeping thousands of such connections open wastes server memory. - **Fan-out to many distinct hosts.** Pools are per origin; a crawler touching a million hosts gets little reuse. - **After failover or scale-out.** Long-lived connections pin traffic to old backends, so servers deliberately recycle them. ## Practical checks Measure rather than assume: `curl -w '%{time_namelookup} %{time_connect} %{time_appconnect} %{time_starttransfer}\n'` separates DNS, TCP, TLS and first-byte time on a real request, and running the same command twice against a keep-alive-enabled endpoint shows what reuse is worth on your network.

  • How does TLS session resumption change the picture, and what is the catch with TLS 1.3 0-RTT early data?
    Resumption reuses a previously negotiated secret via a session ticket, cutting a full handshake to one round trip and skipping the expensive asymmetric signature. TLS 1.3 0-RTT lets the client send application data in its very first flight, giving zero setup round trips. The catch is that 0-RTT data is replayable by an attacker, so it must be restricted to safe, idempotent requests - never a POST that moves money.
  • A client making thousands of short-lived connections per second starts failing to connect while the server looks idle. What is the likely cause?
    Ephemeral port exhaustion on the client, or connection-table pressure in a NAT or load balancer between them. Each closed connection also sits in TIME_WAIT for around a minute on the side that closed first, so the port cannot be reused immediately. The real fix is connection reuse - a pool - rather than widening the port range or shortening TIME_WAIT, which only postpones the problem.

saying these in an interview costs you the question

  • Claiming the cost is 'just one extra round trip' and ignoring TLS entirely
  • Thinking handshake cost is symmetric-crypto work rather than asymmetric signature/key-exchange work
  • Forgetting slow start, so assuming a warm and a cold connection transfer large bodies at the same speed
  • Proposing to shrink TIME_WAIT or enable tcp_tw_reuse as the primary fix for port exhaustion instead of pooling
  • Assuming 0-RTT is free performance with no safety constraint

context