completefrontend
Open lab My progress
THE WEB PERFORMANCE FIELD GUIDE

TCP connections and slow start

How TCP connects, starts small and grows: the handshake, slow start and the first 14 KB, what a lost packet costs, and why reused connections win.

By the end, you can
  • 01
    Count a connection's round tripsSay why a new connection waits one round trip before the request, and work out how many round trips slow start needs for a file.
  • 02
    Use the first 14 KB wellSay where the 14 KB comes from, what counts against it, and why it's a rough guide rather than a hard limit.
  • 03
    Reason about loss and reuseDescribe what a lost packet does to the window and to the data behind it, and why a connection kept open saves time.

A connection before the first byte

DNS lookups ended with the browser holding a number: 199.36.158.100, the address behind completefrontend.com. So now it can ask for the page, right?

Not yet. Here are two times the course recorded on its first visit to completefrontend.com:

The address was known, but the request waitedPop out in the code editor (opens in a new tab)JavaScript
// The course's first visit to completefrontend.com, in ms from the start.const visit = { domainLookupEnd: 26, requestStart: 83.9 }; console.log(`The browser knew the address at ${visit.domainLookupEnd} ms.`);console.log(`It asked for the page at ${visit.requestStart} ms.`);
  • Line 1 is a comment. Each time is in milliseconds (ms), counted from the moment the visit started. A millisecond is a thousandth of a second.
  • Line 2 holds two recorded times. The names come from the browser's own stopwatch, which The navigation timeline reads in full.
  • Line 4 prints The browser knew the address at 26 ms.
  • Line 5 prints It asked for the page at 83.9 ms.

For almost 58 ms, the browser had the address and still didn't ask. What was it waiting for? It was opening a connection to the server. On this site, that takes two steps. The first is TCP, and it's what this lesson is about. The second is TLS, the step that makes the connection private. That's the next lesson.

Here is that visit's connection, drawn to scale. Time goes down:

The course's first visit to completefrontend.com, on two time lines, your browser and the server at 199.36.158.100, with time going down, drawn to scale. At 26 ms the browser knows the address and says hello. The server's hello comes back at 45.6 ms: TCP's hello, one round trip of 19.6 ms. TLS takes the next 38.2 ms. At 83.9 ms, 57.9 ms after it knew the address, the browser asks for the page. The first bytes come back at 104.5 ms, about one round trip later. The rest of the page takes 56.8 ms more, about 3 round trips: it comes in 4 batches, and the browser says got it after each one. The last byte arrives at 161.3 ms.Your browserServer199.36.158.10057.9 mshad the address,didn't ask yet26 msknows the address83.9 msasks for the page104.5 msfirst bytes161.3 mslast byteTLShello?hello!the requestbatch 1got itbatch 2got itbatch 3got itbatch 4TCP's hello: 19.6 ms1 round tripTLS: 38.2 msthe next lessonthe request: 20.6 msabout 1 round tripthe page: 56.8 msabout 3 round trips
Chrome's times for the course's first visit, drawn to scale. The hello took one round trip. The page took about 3 more, in batches, drawn evenly spaced from its first byte to its last. Shaded: what this lesson is about.

A trip across and back is a round trip. The hello took one. The page took about three more, because it came back in batches.

TCP, the Transmission Control Protocol, is the set of rules that carries the page from the server to the browser. Why does a page need rules to travel? Because the network only moves small chunks of data, called packets. And it moves them carelessly. It can lose a packet, deliver one twice, or deliver them out of order.

TCP fixes all of that. It cuts the page into numbered pieces and checks that each one arrives. It sends again whatever goes missing. And it hands the pieces to the browser in the right order. A long letter sent by post needs the same care:

Real-life analogyA long letter in numbered pages

You post a long letter to Ravi, a few pages at a time, and you number every page. Each time some arrive, Ravi texts you: "Got pages 1 to 10." If a page goes missing, you post it again. And Ravi reads the pages in order, even when they arrive mixed up.

In real life: Each numbered page
In JavaScript: A segment: one piece of the page, about 1,460 bytes
In real life: Ravi's text: "Got pages 1 to 10"
In JavaScript: An acknowledgment, or ACK
In real life: Posting a missing page again
In JavaScript: A retransmission
In real life: Reading the pages in order, however they arrive
In JavaScript: TCP handing the bytes to the browser in order

TCP has names for these. A segment is one numbered piece of the page. It holds about 1,460 bytes, and it travels inside a packet. An acknowledgment, or ACK, is the short note the browser sends back to say which segments arrived.

All this care keeps the page whole. But it costs time, in three ways:

  • The handshake. Before any data moves, the browser and the server say hello. That takes one round trip.
  • Slow start. A new connection sends only a little at first, then more each round trip.
  • Loss. A lost packet slows the connection down, and holds up everything behind it.

A round trip is the time it takes a message to reach the server and an answer to come back. Latency vs bandwidth showed that round trips, not megabits, decide how fast most pages load. That's why the picture at the top counts them. The next two sections take it apart: first the hello, then the batches.

The main idea

A new TCP connection spends one round trip saying hello. Then it sends about 14 KB, and doubles that every round trip. A lost packet cuts the speed in half and holds up what comes after it. So keep connections open and reuse them. And put what the page needs first into the first 14 KB.

The three-way handshake

Before a single byte of the page can travel, the browser and the server greet each other. It works like a phone call:

Real-life analogySaying hello before you talk

You phone the tea shop, Leaf and Kettle, to order. You don't start your order the moment you dial. You wait until someone picks up: "Hello, Leaf and Kettle?" You say "Hi!", and then you order straight away. Nothing useful has been said yet, but the call has already taken a few seconds.

In real life: Dialling the tea shop
In JavaScript: SYN: the browser asks to open a connection
In real life: "Hello, Leaf and Kettle?"
In JavaScript: SYN-ACK: the server agrees
In real life: "Hi!", then your order straight away
In JavaScript: ACK, with the request right behind it
In real life: The seconds before your order
In JavaScript: One round trip before the request can leave

TCP opens a connection with three messages, so it's called the three-way handshake:

  • SYN, short for synchronize. The browser asks the server to open a connection.
  • SYN-ACK. The server agrees, and acknowledges the browser's SYN.
  • ACK. The browser acknowledges the server's answer.

Here's the part that saves time. The browser doesn't wait after its ACK. It sends the request right behind it. So three messages cost only one round trip of waiting: the SYN going out, and the SYN-ACK coming back. In the picture at the top, that's the first round trip: hello? is the SYN, and hello! is the SYN-ACK.

On an HTTPS site like this one, TLS's first message takes the request's place behind the ACK. The request follows once TLS is done.

How long is that round trip? Back to the course's first visit:

How long the hello tookPop out in the code editor (opens in a new tab)JavaScript
// The same visit: when TCP started, and when TLS took over.const visit = { connectStart: 26, secureConnectionStart: 45.6 }; const handshakeMs = visit.secureConnectionStart - visit.connectStart;console.log(`The TCP handshake took ${handshakeMs.toFixed(1)} ms.`);
  • Line 2 holds two recorded times. connectStart is when the browser started connecting. secureConnectionStart is when TCP was done and TLS began.
  • Line 4 subtracts the first from the second.
  • Line 5 prints The TCP handshake took 19.6 ms.

That's one round trip to completefrontend.com's server. The rest of the connection, 38.2 ms, went on TLS. So that answers the question from the start: of the almost 58 ms, TCP took 19.6 and TLS took 38.2.

A round trip gets longer with distance. Ilya Grigorik works out an example in his book, High Performance Browser Networking. Between New York and London, light in an optical fibre needs at least 56 ms to go there and back. So a handshake between those two cities takes at least 56 ms, however fast the line is.

Why it matters: no amount of bandwidth makes the handshake shorter. The only way to skip it is to not need a new connection. The section on reuse comes back to this.

One TCP feature trims it. TCP Fast Open lets a repeat connection carry data in its very first message. It has limits, though: on how much data, and on which requests.

Congestion window and slow start

The hello is done, and the request has left. Now the server sends the page. Say the page is 50 KB, and your line is fast. Does the whole page arrive in the next round trip?

No. The server doesn't know how fast your line is. It doesn't know how busy the network in between is, either. If it sends too much at once, the routers on the way, the machines that pass packets along, drop what they can't hold. So it starts small, and learns as it goes, like a new waiter:

Real-life analogyA new waiter's first trips

A café hires a new waiter. On their first trip to the tables, they carry only a few plates. They don't know the room yet, or how crowded it is. Each trip that goes well, they carry twice as many. After a few trips, they carry a full tray.

In real life: The plates on one trip
In JavaScript: The congestion window: how much the server may send before it waits for ACKs
In real life: One trip to the tables and back
In JavaScript: One round trip
In real life: Twice as many plates after each good trip
In JavaScript: Slow start: the window doubles each round trip
In real life: The few plates on the very first trip
In JavaScript: The initial window: 10 segments, about 14 KB

In TCP, the plates on one trip are the congestion window. It's how much the server may send before it stops and waits for ACKs. Congestion means a crowded network, like a crowded road.

The window grows as ACKs come back. For each segment acknowledged, it grows by one segment. In one round trip, every segment sent gets its ACK. So each round trip, the window doubles. This first phase is called slow start. The name is odd, because the window grows fast. It's the start that's small.

Where does it start? Since 2013, a new connection may start with 10 segments, at most 14,600 bytes. That's about 14 KB. The number comes from RFC 6928. RFCs are the numbered documents the internet's rules are written in. The rule before it allowed 2 to 4 segments. This first size is called the initial window. Here's the doubling, one round trip at a time:

The window doubles each round tripPop out in the code editor (opens in a new tab)JavaScript
// A new connection may send 10 segments before it waits.let windowSize = 10; for (let trip = 1; trip <= 4; trip++) {  console.log(`Round trip ${trip}: ${windowSize} segments, ${windowSize * 1460} bytes`);  windowSize = windowSize * 2;}
  • Line 1 is a comment. Line 2 starts the window at 10 segments.
  • Line 4 repeats for four round trips.
  • Line 5 prints the window, in segments and in bytes. Each segment holds 1,460 bytes.
  • Line 6 doubles the window for the next round trip.
  • It prints Round trip 1: 10 segments, 14600 bytes and Round trip 2: 20 segments, 29200 bytes. Then Round trip 3: 40 segments, 58400 bytes and Round trip 4: 80 segments, 116800 bytes.
Four bars, one per round trip on a new connection, growing as the window doubles: 10 segments, 14.6 KB; 20 segments, 29.2 KB; 40 segments, 58.4 KB; 80 segments, 116.8 KB. After four round trips, 219 KB in all.14.6 KBround trip 110 segments14.6 KB so far29.2 KBround trip 220 segments43.8 KB so far58.4 KBround trip 340 segments102.2 KB so far116.8 KBround trip 480 segments219 KB so far
Each bar is one round trip's batch. It starts at 10 segments, about 14 KB, and doubles each time. The last line under each bar adds them up.

So how many round trips does the 50 KB page take? Keep sending windows until the whole file is out:

Round trips for one filePop out in the code editor (opens in a new tab)JavaScript
const fileBytes = 50000;let windowSize = 10;let sent = 0;let trips = 0;while (sent < fileBytes) {  sent += windowSize * 1460;  windowSize *= 2;  trips++;}console.log(`A ${fileBytes / 1000} KB file takes ${trips} round trips.`);
  • Line 1 is the file: 50,000 bytes, or 50 KB.
  • Line 2 starts the window at 10 segments. Lines 3 and 4 count the bytes sent and the round trips.
  • Line 5 repeats until the whole file is sent.
  • Line 6 sends one window. Line 7 doubles it. Line 8 counts one more round trip.
  • Line 10 prints A 50 KB file takes 3 round trips.

Two round trips carry 14.6 + 29.2 = 43.8 KB. That's not quite 50, so a third is needed. Add the round trip for the hello, and the last byte arrives four round trips after the browser started connecting. That's before TLS adds its own.

Why it matters: a faster line doesn't change that count. On a new connection, the window decides how much may go in each round trip, not the line.

The window doesn't double forever. It stops doubling when one of these happens:

  • It reaches the browser's limit. The browser tells the server how much it can take at once. That's the receive window. The server never sends more than it, however big the congestion window gets.
  • It reaches a threshold. TCP can set a size where slow start ends. After it, the window grows by only one segment per round trip.
  • A packet is lost. The section on loss shows what happens then.

Does a bigger start really help? Google tested it on its search pages, and RFC 6928 reports the results. On average, search results arrived 11.7% sooner in one data center, and 8.7% sooner in another. On paths with long round trips, the gain was over 20%. Saving a round trip matters most where round trips are long.

Try it yourself. Pick a file size and a starting window. The third button loses a packet in round trip 3. The section on loss explains what happens then.

Playground: slow start
The model, shortenedJavaScript
let window = initialSegments;let slowStart = true;while (delivered < needed) {  const lost = round === lossAt;  send(window);  delivered += lost ? window - 1 : window;  if (lost) slowStart = false;  window = lost ? window / 2 : slowStart ? window * 2 : window + 1;}
Segments per round trip3 in all
110 (14.6 KB)
220 (29.2 KB)
35 (7.3 KB)
Try it yourself
Round trips

A 50 KB file, starting with 10 segments, takes 3 round trips. At 100 ms each, that's about 300 ms, not counting the handshake.

This is a model, not a measurement. The window doubles each round trip. After a loss, it halves, then grows by one segment per round trip, like classic TCP. Newer methods, such as CUBIC, get back up to speed faster.

In the panel's code:

  • Line 6 counts what got through. A lost segment doesn't count, because it has to be sent again.
  • Line 8 sets the next window. It doubles in slow start. After a loss, it halves, then grows by one segment per round trip.

The 14 KB rule and its limits

The first round trip of data carries about 14 KB. That gives web developers a rough guide, often called the 14 KB rule. Put what the browser needs to start showing the page into the first 14 KB. Then it can start building the page after one round trip of data, instead of waiting for more.

It's the new waiter's first trip again. Bring the drinks and the bread first, so the table can start while the main course is on its way.

What does the page need first? The top of the HTML, and the CSS for the first screen, often written inside the HTML. Here's a quick check for one page:

Does the page's start fit?Pop out in the code editor (opens in a new tab)JavaScript
const firstRoundTrip = 10 * 1460;const headers = 600;const html = 9000;const inlineCss = 4000;const start = headers + html + inlineCss; console.log(`The first round trip carries ${firstRoundTrip} bytes.`);console.log(`This page's start is ${start} bytes, so ${start <= firstRoundTrip ? "it fits" : "it needs another round trip"}.`);
  • Line 1 is the first round trip: 10 segments of 1,460 bytes.
  • Lines 2 to 4 are the parts of this page's start: the response's headers, the HTML, and the CSS written inside it.
  • Line 5 adds them up.
  • Line 7 prints The first round trip carries 14600 bytes.
  • Line 8 prints This page's start is 13600 bytes, so it fits.

Why it matters: when the start fits, the browser has everything the first screen needs after one round trip of data. When it doesn't, part of it comes a round trip later.

Don't count to the byte, though. It's a rough guide, not a law. Read this table across: each row is something that changes the sum, and how it changes it.

What changes the sum
What changesHow it changes the sum
CompressionThe window counts the bytes that travel. So count the compressed size, not the size of the file on disk.
HeadersThe response's headers travel in the same segments as the HTML. They count too.
HTTPSOn a new HTTPS connection, the server first sends its certificates. They travel under the same window, before any HTML.
A bigger startSome servers start with more than 10 segments. A reused connection's window has already grown.

So keep the start of the HTML small and compressed. Leave out anything the first screen doesn't need. Later stages of the course show how.

Packet loss and retransmission

So far, every packet arrived. On a real network, some don't. A router with a full queue drops what it can't hold. A crowded café's Wi-Fi loses some too.

How does TCP notice? The ACKs tell it. Say the browser's ACKs keep saying "I have everything up to segment 2", while later segments arrive. Then segment 3 is missing. And if no ACK comes back for too long, something was lost too.

Then TCP does two things. It sends the missing segment again. That's a retransmission. And it slows down, because it reads the loss as a sign that the network is too full. Think of a garden hose:

Real-life analogyOpening a hose slowly

You water the garden with a hose you haven't used before. You open the tap a little, then more and more, until water sprays from a joint. You turn the tap back to half. Then you open it again, but slowly, a little at a time.

In real life: Opening the tap more and more
In JavaScript: Slow start: the window doubles each round trip
In real life: Water spraying from a joint
In JavaScript: A lost packet
In real life: Turning the tap back to half
In JavaScript: The window halves
In real life: Opening it again, a little at a time
In JavaScript: The window grows by one segment per round trip

That's how classic TCP recovers. Grigorik's book calls it AIMD, for multiplicative decrease and additive increase. In plain words: cut the window in half after a loss, then add one segment at a time. Here it is, with a loss in round trip 3:

What one loss does to the windowPop out in the code editor (opens in a new tab)JavaScript
const lostIn = 3;let windowSize = 10;const sizes = [];for (let trip = 1; trip <= 5; trip++) {  sizes.push(windowSize);  if (trip < lostIn) windowSize *= 2;  else if (trip === lostIn) windowSize /= 2;  else windowSize += 1;}console.log(`A loss in round trip ${lostIn}: ${sizes.join(", ")} segments.`);
  • Line 1 says when the loss happens: in round trip 3.
  • Line 2 starts the window at 10 segments. Line 3 is a list for the window of each round trip.
  • Line 4 repeats for five round trips. Line 5 writes down this round trip's window.
  • Line 6 doubles the window before the loss.
  • Line 7 halves it after the round trip that had the loss.
  • Line 8 adds one segment in every round trip after it.
  • Line 10 prints A loss in round trip 3: 10, 20, 40, 20, 21 segments.
Two rows of five round trips each, as bars of the window in segments. Without a loss: 10, 20, 40, 80, 160. With a loss in round trip 3: 10, 20, 40, then 20 and 21, because the window halves and then grows by one segment per round trip.No loss10204080160A loss in round trip 31020402021
The shaded bar is the round trip with the loss. After it, the window halves, then grows by one segment at a time. By round trip 5, the connection sends 21 segments instead of 160.

Without the loss, round trip 5 would carry 160 segments. With it, only 21. So one lost packet early in a page load costs many round trips later.

Newer methods get back up to speed faster. One of them, CUBIC, is the default in Linux, Windows and Apple's systems, RFC 9438 says. But a loss still costs time.

One loss holds up everything behind it

A loss has a second cost. Remember Ravi reading the letter in order. TCP hands data to the browser in order too. So when segment 3 is lost, segments 4, 5 and 6 can arrive, but the browser doesn't get them yet. They wait in TCP's buffer, a waiting area in memory, until a new copy of segment 3 arrives.

This is called head-of-line blocking. It's like the queue at the tea shop's counter. When the person at the front can't find their purse, nobody behind them gets served.

Six segments in order. Segments 1 and 2 are handed to the browser. Segment 3 is lost. Segments 4, 5 and 6 arrive, but wait in TCP's buffer until segment 3 is sent again and arrives.1delivered2deliveredlostsent again4waiting5waiting6waitingSegments in the order they were sent
TCP hands data to the browser in order. Segments 4, 5 and 6 have arrived, but they wait for a new copy of segment 3, even if they belong to a different file.

Why it matters: HTTP/2 sends many files over one TCP connection. HTTP/2 is a version of HTTP, the language browsers and servers speak, and this site's first visit used it. But TCP doesn't know about files. It only sees bytes in order. So one lost packet stalls every file on the connection, even the ones it has nothing to do with. HTTP/3 solves this, and HTTP/3 and QUIC shows how.

Keeping connections alive and reused

Every new connection pays twice. It spends a round trip saying hello, and it starts with a small window that has to grow. A connection that stays open pays neither again.

Real-life analogyStaying on the line

You have six questions for the tea shop. You could ask one, hang up, then dial again for the next. Every call would start with the ringing and the "Hello?". Or you could stay on the line and ask all six. By the third question, you're both talking at full speed.

In real life: Hanging up and dialling again
In JavaScript: A new connection for each request: a handshake every time
In real life: Staying on the line
In JavaScript: Keep-alive: one connection for many requests
In real life: Talking at full speed by the third question
In JavaScript: A window that has already grown

HTTP/1.1 keeps a connection open after a response, unless told otherwise. This is called keep-alive, or a persistent connection. The next request skips the handshake, and finds a window that has already grown. Here's what the hello alone costs six requests, with each round trip at 100 ms:

A new connection each time, or one kept alivePop out in the code editor (opens in a new tab)JavaScript
const roundTripMs = 100;const requests = 6; const newEachTime = requests * 2 * roundTripMs;const keptAlive = 2 * roundTripMs + (requests - 1) * roundTripMs; console.log(`A new connection for each request: ${newEachTime} ms.`);console.log(`One connection, kept alive: ${keptAlive} ms.`);console.log(`Keep-alive saved ${newEachTime - keptAlive} ms.`);
  • Line 1 sets one round trip to 100 ms. Line 2 is the number of requests.
  • Line 4 gives each request its own connection. Each one takes two round trips: one for the hello, one for the request and its answer.
  • Line 5 keeps one connection open. The first request takes two round trips, and each of the other five takes one.
  • Lines 7 and 8 print A new connection for each request: 1200 ms. and One connection, kept alive: 700 ms.
  • Line 9 prints Keep-alive saved 500 ms.

Grigorik puts the saving at (N − 1) × RTT for N requests. RTT is short for round trip time. In words: one round trip for every request after the first. And this sum leaves out slow start, which a kept connection also skips.

How many connections does a browser open? On HTTP/1.1, a connection carries one request at a time. So browsers open up to six connections to each host. Each of them pays its own handshake and its own slow start. HTTP/2 and HTTP/3 send all of a host's files over one connection. HTTP/1.1 and HTTP/2 compares them.

An open connection can also cool down. If it sits idle for a while, TCP may shrink its window back to the start. It's like a waiter back from a long break, who starts again with a few plates. This is called slow-start restart. Grigorik recommends turning it off on servers, so that connections kept open stay fast.

Which of this page's own files reused a connection? Press Look:

Playground: this page's connections
What Look does, shortenedJavaScript
const [page] = performance.getEntriesByType("navigation");console.log(page.nextHopProtocol, page.transferSize, "bytes"); for (const file of performance.getEntriesByType("resource")) {  const connect = file.connectEnd - file.connectStart;  const kind = file.connectStart === 0 ? "hidden" : connect > 0 ? "new connection" : "reused";  count(kind, file.nextHopProtocol);}
This pagenot read yet

Nothing read yet.

Try it yourself
Connections

Press Look. You'll see how this page reached your browser: the protocol, the size of its HTML on the network, and which files needed a new connection.

Read from your own browser with Navigation Timing and Resource Timing. A file whose connectStart and connectEnd are equal reused a connection. Another site's files show nothing unless that site sends a Timing-Allow-Origin header.

In the panel's code:

  • Line 2 reads the page's protocol, and how many bytes its HTML took on the network.
  • Line 5 works out how long each file spent connecting.
  • Line 6 sorts the files. No time connecting means the file reused a connection. A zero connectStart means another site hid its timings. A site shares them only if it sends a Timing-Allow-Origin header.

What is it costing?

Each delay below comes mostly from one of TCP's three costs. For each one, ask: is the page waiting to say hello, waiting for the window to grow, or waiting for a lost packet?

What is it costing?
  • A font from a host the page hasn't connected to yet
  • A server that closes the connection after every response
  • A 500 KB file on a brand-new connection
  • HTML that's 40 KB compressed, on a first visit
  • A connection that sat idle, then sends a large response
  • Crowded café Wi-Fi that drops packets
  • Every file on an HTTP/2 connection stalls while one packet is sent again
Try it yourself
0 of 7 correct

Sort each delay by the TCP cost behind it.

Choose a category for every card. You can change an answer at any time; Reset clears them all.

Common mix-ups

  • "Bandwidth decides how fast a big file starts." No. A new connection sends about 14 KB in its first round trip, however fast the line is. The window grows from there.
  • "The 14 KB rule means pages must be under 14 KB." No. It's about what the first round trip carries. The rest of the page just takes more round trips.
  • "The handshake takes three round trips." No. It's three messages, but one round trip. The request leaves right behind the last one.
  • "Packet loss only delays the lost packet." No. It halves the window. And everything behind the lost packet waits for it.
  • "More connections are always faster." No. Each new one pays its own handshake and slow start, and they all share the same line.
  • "A connection's speed is set when it opens." No. It grows in slow start, shrinks after a loss, and can start over after sitting idle.

Practice exercises

Answer first, then use the hints.

Exercise 1 · Warm-upBefore 2013

Servers used to start with 4 segments. In this copy of the round trips example, line 2 starts with 4. What does it print?

Starter codePop out in the code editor (opens in a new tab)JavaScript
const fileBytes = 50000;
let windowSize = 4;
let sent = 0;
let trips = 0;
while (sent < fileBytes) {
  sent += windowSize * 1460;
  windowSize *= 2;
  trips++;
}
console.log(`A ${fileBytes / 1000} KB file takes ${trips} round trips.`);

Answer, then press Check. Spacing and letter case don’t matter.

    Exercise 2 · Warm-upTen requests

    The page now makes ten requests. What does the last line print?

    Starter codePop out in the code editor (opens in a new tab)JavaScript
    const roundTripMs = 100;
    const requests = 10;
    
    const newEachTime = requests * 2 * roundTripMs;
    const keptAlive = 2 * roundTripMs + (requests - 1) * roundTripMs;
    
    console.log(`A new connection for each request: ${newEachTime} ms.`);
    console.log(`One connection, kept alive: ${keptAlive} ms.`);
    console.log(`Keep-alive saved ${newEachTime - keptAlive} ms.`);

    Answer, then press Check. Spacing and letter case don’t matter.

      Exercise 3 · PracticeA bigger start

      The HTML grows to 12,000 bytes. What does the last line print?

      Starter codePop out in the code editor (opens in a new tab)JavaScript
      const firstRoundTrip = 10 * 1460;
      const headers = 600;
      const html = 12000;
      const inlineCss = 4000;
      const start = headers + html + inlineCss;
      
      console.log(`The first round trip carries ${firstRoundTrip} bytes.`);
      console.log(`This page's start is ${start} bytes, so ${start <= firstRoundTrip ? "it fits" : "it needs another round trip"}.`);

      Answer, then press Check. Spacing and letter case don’t matter.

        Exercise 4 · PracticeAn early loss

        This time, the packet is lost in round trip 2. What does it print?

        Starter codePop out in the code editor (opens in a new tab)JavaScript
        const lostIn = 2;
        let windowSize = 10;
        const sizes = [];
        for (let trip = 1; trip <= 5; trip++) {
          sizes.push(windowSize);
          if (trip < lostIn) windowSize *= 2;
          else if (trip === lostIn) windowSize /= 2;
          else windowSize += 1;
        }
        console.log(`A loss in round trip ${lostIn}: ${sizes.join(", ")} segments.`);

        Answer, then press Check. Spacing and letter case don’t matter.

          Exercise 5 · ChallengeThe handshake in DevTools

          Open DevTools' Network panel and reload a page. Click a request that needed a new connection, and open its Timing tab. What is the phase for the TCP handshake called?

          Answer, then press Check. Spacing and letter case don’t matter.

            Exercise 6 · ChallengeReused connections

            In the connections playground, why do most of this site's own files show a reused connection?

            Answer, then press Check. Spacing and letter case don’t matter.

              Check your understanding

              For each question, ask which cost it's about: the hello, the growing window, or a lost packet.

              The TCP and slow start quiz · 7 questionsScore: first tries count
              1. Question 1 of 7This is the round trips example with a 200 KB file. What does it print?

                Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
                const fileBytes = 200000;
                let windowSize = 10;
                let sent = 0;
                let trips = 0;
                while (sent < fileBytes) {
                  sent += windowSize * 1460;
                  windowSize *= 2;
                  trips++;
                }
                console.log(`A ${fileBytes / 1000} KB file takes ${trips} round trips.`);

                Choose an answer to see the explanation.

              2. Question 2 of 7How long does TCP's three-way handshake hold up the first request?

                Choose an answer to see the explanation.

              3. Question 3 of 7Where does the 14 KB in the 14 KB rule come from?

                Choose an answer to see the explanation.

              4. Question 4 of 7This is the loss example with the loss in round trip 4. What does it print?

                Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
                const lostIn = 4;
                let windowSize = 10;
                const sizes = [];
                for (let trip = 1; trip <= 5; trip++) {
                  sizes.push(windowSize);
                  if (trip < lostIn) windowSize *= 2;
                  else if (trip === lostIn) windowSize /= 2;
                  else windowSize += 1;
                }
                console.log(`A loss in round trip ${lostIn}: ${sizes.join(", ")} segments.`);

                Choose an answer to see the explanation.

              5. Question 5 of 7What is TCP head-of-line blocking?

                Choose an answer to see the explanation.

              6. Question 6 of 7What does the last line of the keep-alive example print?

                Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
                const roundTripMs = 100;
                const requests = 6;
                
                const newEachTime = requests * 2 * roundTripMs;
                const keptAlive = 2 * roundTripMs + (requests - 1) * roundTripMs;
                
                console.log(`A new connection for each request: ${newEachTime} ms.`);
                console.log(`One connection, kept alive: ${keptAlive} ms.`);
                console.log(`Keep-alive saved ${newEachTime - keptAlive} ms.`);

                Choose an answer to see the explanation.

              7. Question 7 of 7Why does a big file load faster on a connection that's already been in use?

                Choose an answer to see the explanation.

              Key takeaways

              • A new TCP connection spends one round trip saying hello before the request can leave.
              • Slow start begins at 10 segments, about 14 KB, and doubles every round trip.
              • A faster line doesn't remove those round trips. The window decides how much goes each time.
              • Put what the first screen needs into the first 14 KB, counted compressed and with headers.
              • A lost packet halves the window, and holds up everything behind it.
              • Keep-alive saves a round trip for every request after the first. Idle connections can cool down.

              Remember the one-liner.
              Connect once, start small, grow fast, and don't lose anything.

              Sources

              The facts on this page come from these standards, books and docs:

              Coming next: TLS without the wait. Remember the other 38.2 ms of the first visit's connection? That was TLS, making the line private. TLS 1.3, resumption and good certificates make most of its round trips disappear.

              CompleteFrontend Clear concepts. Working examples.