Why is the first request always slower? The TCP handshake costs a full RTT
TL;DR
Before the first byte of data can be sent, client and server must complete the TCP handshake, costing one full round trip (RTT). Follow-up requests reuse the open connection (keep-alive), so they’re exactly one RTT faster.
Timeline with an 80ms RTT
| Time | Event |
|---|---|
| 0 → 40ms | Client sends SYN to the server. |
| 40 → 80ms | Server replies SYN-ACK (ack = SYN’s seq + 1). |
| 80ms | Client sends ACK together with GET / — only now does the first data byte leave. |
| 160ms | The first response arrives back at the client. |
| +80ms | A second request (GET /app.js) reuses the connection: just 1 RTT. |
How to put it to work
- Reuse connections: Enable keep-alive and connection pooling (HTTP clients, DB drivers) to avoid repeated handshakes.
- Frontend: Use
<link rel="preconnect">for critical origins (fonts, API, CDN) to start the handshake early. - Infrastructure: HTTP/2 multiplexes many requests over one connection; HTTP/3 (QUIC) combines transport and TLS handshakes to cut round trips. Moving servers closer to users (CDN/edge) shortens the RTT itself.
- When measuring: Separate connection time from server processing time so you don’t blame the backend unfairly.