# TCP Slow Start: Why Your First Megabyte Is Always Slow

> A fresh TCP connection doesn't blast data at line rate — it tiptoes. Slow start is the reason a cold connection feels sluggish, and understanding it explains half of web performance.

URL: https://harshith.in/blog/tcp-slow-start/
Author: Harshith Sunku (Staff Engineer) — https://harshith.in/about/
Tags: networking, performance

You have a 1 Gbps link and a 5 ms round trip to the server, yet the first chunk
of a download crawls. The link isn't the bottleneck and neither is the server.
It's TCP deliberately holding back, because a brand-new connection has no idea how
much the network can take.

## The Window You Don't Configure

Throughput on a TCP connection isn't governed by bandwidth directly — it's
governed by how many bytes you're allowed to have *in flight* (sent but not yet
acknowledged). That limit is the **congestion window** (`cwnd`), and the sender
maintains it privately. It is not the receive window you see in packet captures;
it's the sender's own guess about network capacity.

The achievable rate is simply:

```text
   throughput ≈ cwnd / RTT
```

Double the window, double the rate. Double the round trip, halve it. This is why
latency, not bandwidth, dominates short transfers.

## The Exponential Ramp

A new connection starts with a small initial window — typically 10 segments
(~14 KB). Every time a window's worth of data is acknowledged, `cwnd` roughly
doubles:

```text
   RTT 1:  10 segments   (~14 KB)
   RTT 2:  20 segments
   RTT 3:  40 segments
   RTT 4:  80 segments
   ...
```

"Slow" start is a misnomer — the growth is exponential. But it *starts* small, so
it takes several round trips to reach the link's capacity. With a 100 ms RTT,
climbing to a 1 MB window takes ~7 round trips, or most of a second, before
you're using the pipe.

## Why It Hurts the Web

Most web objects are small enough to finish *during* slow start, before the
window ever opens up. The connection never reaches steady state. So page load
time is dominated by:

- **Round trips**, not bandwidth — each one only lets the window double.
- **New connections**, each of which restarts the ramp from the initial window.

```text
   small file on a fast link:   [ slow start ] done
                                 └─ never reaches full rate

   large file on a fast link:   [ slow start ][ full rate .......... ]
                                              └─ ramp amortized away
```

## What Actually Helps

- **Reuse connections.** HTTP keep-alive and connection pooling keep `cwnd`
  warm so the next request skips the ramp. This is a big reason HTTP/2's single
  multiplexed connection beats many short HTTP/1.1 ones.
- **Terminate TLS close to the user.** A CDN edge cuts RTT, and since the ramp is
  measured in round trips, shorter RTT means a faster climb.
- **Don't open six connections** to dodge slow start — modern stacks share
  congestion state, and you mostly just multiply overhead.

## Takeaways

- Throughput is `cwnd / RTT`; a connection is only as fast as its window allows.
- Slow start ramps the window exponentially but begins small, so short transfers
  finish before reaching full speed.
- Web performance is round-trip-bound: reuse connections and cut RTT instead of
  chasing raw bandwidth.
