completefrontend
Open lab My progress
THE WEB PERFORMANCE FIELD GUIDE

TLS without the wait

HTTPS costs round trips. See real TLS 1.2 and 1.3 handshakes timed, and how resumption, smaller certificates and HSTS make most of the cost disappear.

By the end, you can
  • 01
    Count TLS's round tripsSay how many round trips TLS 1.2 and TLS 1.3 add before the request, and what resuming a session and 0-RTT change.
  • 02
    Keep certificates smallExplain why the certificate chain's size matters on a new connection, and why OCSP stapling matters less than it used to.
  • 03
    Skip the http:// detourSay what a redirect from http:// to https:// costs, and how HSTS and the browser's own upgrades skip it.

HTTPS costs round trips

Back to your first visit to completefrontend.com. The browser has found the server's address, as DNS lookups showed. It has opened a line to the server with TCP, as TCP connections and slow start showed. It could ask for the page now. But it doesn't, not yet.

The address starts with https://, and the S stands for secure. On its way, your request passes through computers you don't know: your Wi-Fi router, your internet provider, and others. None of them should be able to read it or change it. So before the browser says a word, it and the server agree on a secret code. That takes a few messages there and back, and each one is a wait.

Here is how long that took when the course connected to completefrontend.com from its own computer:

Opening the line, then agreeing on a codePop out in the code editor (opens in a new tab)JavaScript
// Recorded from the course's computer to completefrontend.com.const tcpMs = 23.3; // opening the lineconst tlsMs = 28.5; // agreeing on the secret code, with TLS 1.3 console.log(`Opening the line took ${tcpMs} ms.`);console.log(`Agreeing on the code took ${tlsMs} ms more.`);console.log(`The request could leave after ${(tcpMs + tlsMs).toFixed(1)} ms.`);
  • Line 1 is a comment. The course connected five times and kept the middle time for each step, so one slow try doesn't change the answer.
  • Line 2 is how long TCP took to open the line. Line 3 is how long the secret code took after that.
  • Lines 5 and 6 print them: Opening the line took 23.3 ms. and Agreeing on the code took 28.5 ms more.
  • Line 7 adds them up. It prints The request could leave after 51.8 ms.

Here is that first visit, from your browser to the server, with the wait drawn to scale at the bottom:

Your first visit to completefrontend.com. Your browser on the left, the server on the right, and between them the computers on the way: your Wi-Fi router, your internet provider, and others. Time runs down. The request waits in your browser. TCP: the browser sends "open a line?". TCP: the server answers "yes, it's open". TLS 1.3: the browser sends "hello + key share". TLS 1.3: the server answers "key share + certificate". the request: the browser sends "ready + the request". the request: the server answers "the page". Every message passes the computers on the way. A bar at the bottom shows the recorded wait to scale: 23.3 ms opening the line with TCP, then 28.5 ms more agreeing on the code with TLS 1.3. The request leaves at 51.8 ms.strangers on the wayYour browserServerWi-Firouterinternetproviderothersthe requestwaits hereopen a line?yes, it's openhello + key sharekey share + certificateready + the requestthe pageTCP1 round tripTLS 1.31 round tripthe requestleaves at 51.8 msthe wait in your browser, to scaleopening the line: 23.3 msagreeing on the code: 28.5 ms0 ms51.8 msthe request leaves
Time runs down, and every message passes the computers on the way. The request waits in your browser until the line is open and the code is agreed. The bar at the bottom is to scale: agreeing on the code took more than half the wait.

Your request sits in your browser the whole time. Every message before it passes the strangers on the way.

So agreeing on the code took longer than opening the line. It was more than half of the wait, and the browser hadn't asked for anything yet. Why does it take so long? And how can a site make it shorter? The rest of the lesson answers both questions.

Real-life analogyAgreeing on a code before you read out your card

You phone a shop to pay for an order. Before you read out your card number, you and the shop agree on a secret code, so anyone listening on the line hears only nonsense. Agreeing takes a little back-and-forth, and you can't read out the number until it's done.

In real life: The call connects, and someone says "Hello?"
In JavaScript: TCP has opened the line
In real life: Agreeing on a secret code first
In JavaScript: The TLS handshake
In real life: Each time you speak and wait for an answer
In JavaScript: A round trip
In real life: Reading out your card number, in code
In JavaScript: The request, sent once TLS is done

That's the job of TLS, short for Transport Layer Security. It's the set of rules a browser and a server follow to agree on a secret code, and then to use it. The back-and-forth at the start is the TLS handshake.

A message and the answer it waits for make one round trip. In Latency vs bandwidth, round trips, not megabits, decided how fast Ravi's page loaded. Count them in the first picture of this lesson, from the top down:

  • The first round trip is TCP. The browser asks to open a line, and the server says yes.
  • The second round trip is the TLS handshake. Each side sends a key share, its part in making the code. The server also sends its certificate, its proof that it really is completefrontend.com.
  • Only in the third round trip does the request go out, and the page starts coming back.

So the handshake costs three things:

  • Round trips. Every message that waits for an answer is a round trip. TLS 1.3, the newest version, needs one before the request can leave. The older TLS 1.2 needs two.
  • Bytes. The certificate travels in the server's first answer, and it takes up room there.
  • Work. Both computers do some maths to make the code and to check the certificate. That takes a few milliseconds.
The main idea

Before the first request on a new HTTPS connection, the browser and the server agree on a secret code. That costs round trips: two with TLS 1.2, and one with TLS 1.3. On a return visit, TLS 1.3 can even send the request with its very first message. A site makes TLS cheaper in four ways: turn on TLS 1.3, let browsers resume sessions, keep the certificates small, and skip the detour through http://.

Why do one or two round trips matter? Remember Ravi. Every message he sent took 150 ms to reach the server and come back. So each round trip TLS saves lets him ask for the page 150 ms sooner. And it isn't just once per page. Each new host a page uses usually needs its own connection, with its own handshake.

The TLS 1.2 and TLS 1.3 handshakes

Why does TLS 1.2 need two round trips, and TLS 1.3 only one? The answer is in how the two sides agree on a code that nobody listening can learn.

Agreeing on a code in public

The two sides can't just send the code to each other. Anyone listening would see it. So they do this instead:

  1. Each side makes up a secret number, and never sends it.
  2. From its secret, each side works out a key share: a number that's safe to send. It sends that.
  3. Each side mixes its own secret with the other side's key share. The maths is chosen so that both get the same result: the key, the secret code for everything after.

Someone listening sees both key shares, but neither secret, so they can't work out the key. For speed, one thing matters: nothing can be sent in code until both key shares have crossed.

TLS 1.2: ask first, then swap

TLS 1.2 takes it slowly, in two round trips:

  1. The browser says hello, and lists the kinds of code it can use. The server picks one, and answers with its certificate and its key share.
  2. The browser sends its key share. Both sides work out the key, and each confirms it got the same one.

Only then can the request leave. On Ravi's line, that's a long wait:

TLS 1.2 on Ravi's linePop out in the code editor (opens in a new tab)JavaScript
const roundTripMs = 150; // Ravi's round tripconst tcpTrips = 1; // opening the lineconst tlsTrips = 2; // TLS 1.2's handshake const waitMs = (tcpTrips + tlsTrips) * roundTripMs;console.log(`With TLS 1.2, the request waits ${waitMs} ms.`);
  • Line 1 is Ravi's round trip: 150 ms.
  • Lines 2 and 3 count the round trips before the request: one to open the line, and two for TLS 1.2.
  • Line 5 adds them up, and multiplies by the round trip.
  • Line 6 prints With TLS 1.2, the request waits 450 ms.

TLS 1.3: guess, and swap at once

TLS 1.3 was published in 2018, in a standard called RFC 8446. It saves a round trip by guessing. In its very first message, the browser says hello and sends its key share, for the kind of code the server will most likely pick. The server answers with its own key share, its certificate, and "ready". Both key shares have crossed in one round trip.

It's like suggesting a time in your first message to a friend, instead of first asking which day suits them. If the guess is wrong, the server asks for another key share, and that costs an extra round trip.

TLS 1.3 on Ravi's linePop out in the code editor (opens in a new tab)JavaScript
const roundTripMs = 150; // Ravi's round tripconst tcpTrips = 1; // opening the lineconst tlsTrips = 1; // TLS 1.3's handshake const waitMs = (tcpTrips + tlsTrips) * roundTripMs;console.log(`With TLS 1.3, the request waits ${waitMs} ms.`);
  • Only line 3 changes, and the name on line 6. TLS 1.3 needs one round trip, not two.
  • Line 6 prints With TLS 1.3, the request waits 300 ms.

That's 150 ms saved for Ravi, on every new connection. If your server still speaks only TLS 1.2, turning on TLS 1.3 gives the same saving to every visitor whose browser supports it. The picture puts the two side by side:

Two rows of round trips before the first response. TLS 1.2: one for TCP, two for TLS, then the request. TLS 1.3: one for TCP, one for TLS, then the request. Recorded to completefrontend.com: TLS 1.2's handshake took 52 ms and TLS 1.3's 29 ms, with a 23 ms round trip.TLS 1.2TCPTLSTLSrequestTLS: 52 msTLS 1.3TCPTLSrequestTLS: 29 ms
Each box is one round trip. TLS 1.3 needs one box fewer before the request can leave, and the recorded times on the right agree.

What the recording shows

Do real handshakes agree with the counting? The course timed both versions from its own computer to completefrontend.com. It also timed a plain TCP connection there, which takes one round trip:

Real handshakes, in round tripsPop out in the code editor (opens in a new tab)JavaScript
// Recorded from the course's computer to completefrontend.com.const roundTripMs = 23.3; // one TCP round tripconst tls12Ms = 51.8; // a full TLS 1.2 handshakeconst tls13Ms = 28.5; // a full TLS 1.3 handshake console.log(`TLS 1.2 took ${tls12Ms} ms, ${(tls12Ms / roundTripMs).toFixed(1)} round trips.`);console.log(`TLS 1.3 took ${tls13Ms} ms, ${(tls13Ms / roundTripMs).toFixed(1)} round trips.`);
  • Line 2 is the round trip: 23.3 ms.
  • Lines 3 and 4 are the full handshakes: 51.8 ms for TLS 1.2, and 28.5 ms for TLS 1.3.
  • Lines 6 and 7 divide each one by the round trip.
  • They print TLS 1.2 took 51.8 ms, 2.2 round trips. and TLS 1.3 took 28.5 ms, 1.2 round trips.

Two round trips and one, just as counted. In both, about 5 ms is left over after the whole round trips. That's mostly the work: the maths, and checking the certificate. A few milliseconds of work is small next to a round trip, so the round trips are the part to cut.

TLS 1.2 has one trick of its own, an extension called False Start. It lets the browser send its request right after its own "ready", without waiting for the server's. That cuts TLS 1.2 to one round trip too. TLS 1.3 doesn't need the trick.

Now try other lines. Pick a handshake and a round trip. The panel counts the round trips before the request, the same way as the examples:

Playground: handshakes on different networks
What the panel works outJavaScript
// For the handshake and the round trip you pick:const tcpTrips = 1;const tlsTrips = handshake.trips; // 2, 1 or 0 const waitMs = (tcpTrips + tlsTrips) * roundTripMs;
TLS 1.2, first visit23 ms round trip
TCP23 ms
TLS47 ms
The request leaves after70 ms
Recorded TLS time52 ms
Try it yourself
Before the request

TLS 1.2, first visit. TCP takes one round trip. TLS adds two more. So the request waits 70 ms at a 23 ms round trip. When the course recorded this handshake to completefrontend.com, the TLS part took 52 ms.

The round trips are counted the way the standards describe each handshake. The recorded times are real handshakes from the course's computer to completefrontend.com, the middle of 5 tries.

Line 3 of the panel's code takes the TLS round trips of the handshake you picked. Line 5 is the same sum as in the examples. Try the slowest line with each first visit, and see how much the version matters there.

Your own handshake

Your browser did a handshake to load this page, and it wrote down when each step started and ended. Press Look to read those times:

Playground: this page's TLS
What Look does, shortenedJavaScript
const [page] = performance.getEntriesByType("navigation"); const tcp = page.secureConnectionStart - page.connectStart;const tls = page.connectEnd - page.secureConnectionStart;console.log(page.nextHopProtocol, tcp, tls);
This pagenot read yet

Nothing read yet.

Try it yourself
Your connection

Press Look to read this page's own connection. You'll see how long TCP and TLS took when it loaded, and how many of its files needed a TLS handshake of their own.

Read from your own browser. secureConnectionStart is when TLS began, and connectEnd is when the connection was ready.
  • Line 1 gets the page's list of times, called its navigation entry.
  • Line 3 is TCP. It runs from connectStart, when the connection began, to secureConnectionStart, when TLS began.
  • Line 4 is TLS. It runs from secureConnectionStart to connectEnd, when the connection was ready.

If TLS shows no time, the browser didn't need a new handshake for this page. It used a connection that was already open, and that's the cheapest handshake of all.

Session resumption and 0-RTT

You read this page, close the tab, and come back later. The old connection has been closed, so the browser needs a new one. Does it have to do the whole handshake again, certificate and all?

Real-life analogyYour room key at a hotel

The first time you arrive at a hotel, the desk checks your ID, takes your details, and gives you a room key. When you come back in the evening, you just show the key and walk on. The checking was done once, and the key proves it.

In real life: Showing your ID at the desk on the first day
In JavaScript: A full handshake, with the certificate checked
In real life: The room key the desk gives you
In JavaScript: The session ticket
In real life: Showing just the key when you come back
In JavaScript: Session resumption

TLS works the same way. After a full handshake, the server gives the browser a session ticket: a small, sealed note that only the server can open. It records what the two of them agreed. Next time, the browser shows the ticket in its first message. This is called session resumption, and it saves three things:

  • The server doesn't send its certificate again, so there are fewer bytes.
  • Nobody has to check a signature, so there's less work.
  • With TLS 1.2, the handshake needs one round trip instead of two.

Browsers use the same trick within one visit. When a page needs several connections to one host, the browser lets the first handshake finish. Then the other connections resume its session.

Here is what resuming saved in the recording, with TLS 1.2:

What resuming saved, with TLS 1.2Pop out in the code editor (opens in a new tab)JavaScript
// TLS 1.2 to completefrontend.com, recorded.const roundTripMs = 23.3;const fullMs = 51.8; // the first handshakeconst resumedMs = 25; // the next one, with the ticket const savedMs = fullMs - resumedMs;console.log(`Resuming saved ${savedMs.toFixed(1)} ms, ${(savedMs / roundTripMs).toFixed(1)} round trips.`);
  • Line 2 is the same 23.3 ms round trip.
  • Line 3 is the first, full handshake: 51.8 ms. Line 4 is the next one, with the ticket: 25 ms.
  • Line 6 subtracts the two.
  • Line 7 prints Resuming saved 26.8 ms, 1.2 round trips.

That's one whole round trip, plus a little work. So with TLS 1.2, a returning visitor's browser can send its request one round trip sooner.

What about TLS 1.3? Its full handshake is already one round trip. A resumed one still sends a hello and waits for the answer: one round trip too. So with TLS 1.3, resuming saves bytes and work, but not a round trip. To save that last round trip, TLS 1.3 has one more trick.

Real-life analogyA regular's usual order

At your favourite café, the barista knows you. As you walk in, you call out "The usual!", and your tea is started before you even reach the counter. But if someone recorded your call and played it again, the barista would make a second tea.

In real life: Being a regular the barista knows
In JavaScript: Having a session ticket from an earlier visit
In real life: Calling out "The usual!" as you walk in
In JavaScript: Sending the request with the first TLS message
In real life: Your tea started before you reach the counter
In JavaScript: 0-RTT: no TLS round trip before the request
In real life: Someone playing a recording of your call
In JavaScript: A copy of the early request, sent again

That trick is 0-RTT, short for zero round-trip time. On a resumed connection, the browser makes a key from the ticket. Then it sends its request in the same message as its hello, already in code. Data sent this way is called early data. TLS adds no round trip at all before the request:

0-RTT on Ravi's linePop out in the code editor (opens in a new tab)JavaScript
const roundTripMs = 150; // Ravi's round tripconst tcpTrips = 1; // opening the lineconst tlsTrips = 0; // 0-RTT: the request goes with the hello const waitMs = (tcpTrips + tlsTrips) * roundTripMs;console.log(`With 0-RTT, the request waits ${waitMs} ms.`);
  • Line 3 now counts no round trips for TLS. The name on line 6 changes too.
  • Line 6 prints With 0-RTT, the request waits 150 ms. Only TCP's round trip is left.

There's a catch, and RFC 8446 spends a whole section on it. Someone on the network can record that early data and send it again. The server can't always tell the copy from the first. For "send me the home page", that's harmless: the same page goes out twice. For "place this order", it could mean two orders. So a server should accept early data only for requests that are safe to repeat.

Resuming and 0-RTT are easy to mix up. Read this table across: each row is one way to set up TLS, how many round trips TLS spends before the request, and what it skips.

Five ways to set up TLS
Which handshakeHow many TLS round trips before the request?What does it skip?
TLS 1.2, first visitTwo.Nothing. The certificate is sent and checked.
TLS 1.3, first visitOne.Nothing. The certificate is sent and checked.
TLS 1.2, resumedOne.The certificate and the signature checks.
TLS 1.3, resumedOne.The certificate and the signature checks.
TLS 1.3, 0-RTTNone. The request goes with the hello.The certificate, the checks, and the wait.

Certificate chains and OCSP stapling

In the handshake, the server sends its certificate. What is that, exactly? And why does its size matter for speed?

Real-life analogyA letter of introduction

You visit an office where nobody knows you. You hand the receptionist a letter saying who you are, signed by your manager, and a note from head office saying your manager is real. She knows head office's signature, so she believes the whole pile. But she reads every page before she lets you in.

In real life: Your letter, signed by your manager
In JavaScript: The site's certificate, signed by an intermediate
In real life: Head office's note that your manager is real
In JavaScript: The intermediate's certificate, signed by a root
In real life: Head office's signature, which the receptionist already knows
In JavaScript: The root certificate, already in the browser
In real life: Every page she has to read
In JavaScript: Every byte of the chain, sent before the page

A certificate is a small file that says "this key belongs to completefrontend.com". A company that browsers trust signs it. That company is a certificate authority.

Usually the signer isn't trusted directly. It has a certificate of its own, signed by a bigger authority. At the top is a root: a certificate that's built into your browser. The certificates in between are called intermediates. The site's certificate plus its intermediates make the certificate chain, and the server sends all of it.

The chain's share of the first round trip

Why does the size matter? TCP connections and slow start showed that a new connection starts small. In its first round trip, a server can send only about 14,600 bytes. Then it has to wait for the browser to say "got it".

The chain travels in the server's first answer. If that answer is bigger than 14,600 bytes, the server has to stop and wait, and the handshake costs a whole extra round trip. So how much room does the chain take?

This site's chainPop out in the code editor (opens in a new tab)JavaScript
const firstRoundTripBytes = 14600; // what a new connection can send at firstconst chainBytes = 6093; // the 3 certificates completefrontend.com sends const share = Math.round((chainBytes / firstRoundTripBytes) * 100);console.log(`The certificates fill ${share}% of the first round trip.`);
  • Line 1 is the room in the first round trip: 14,600 bytes.
  • Line 2 is this site's recorded chain: 3 certificates, 6,093 bytes.
  • Line 4 works out the chain's share, as a percentage.
  • Line 5 prints The certificates fill 42% of the first round trip.

It fits, with room to spare. That's about 2 KB for each certificate. A longer chain, or bigger certificates, could fill the room and cost that extra round trip.

The size of a certificate depends on its kind of key. Many certificates use RSA keys, and this site's do too. Newer ones can use ECDSA keys, which are much smaller for the same safety. cloudflare.com uses ECDSA, and the course recorded its chain too:

An ECDSA chainPop out in the code editor (opens in a new tab)JavaScript
const firstRoundTripBytes = 14600; // what a new connection can send at firstconst chainBytes = 2558; // the 3 certificates cloudflare.com sends const share = Math.round((chainBytes / firstRoundTripBytes) * 100);console.log(`The certificates fill ${share}% of the first round trip.`);
  • Only line 2 changes: 3 certificates again, but 2,558 bytes.
  • Line 5 prints The certificates fill 18% of the first round trip.

That's the same number of certificates in less than half the bytes. Here are both chains, drawn to scale:

Two bars, each the 14,600 bytes a server can send in a new connection's first round trip. completefrontend.com's RSA chain fills 6,093 bytes of it, 42%. cloudflare.com's ECDSA chain fills 2,558 bytes of it, 18%.the first round trip: 14,600 bytescompletefrontend.comRSA chain42%6,093 bytescloudflare.comECDSA chain18%2,558 bytes
Each bar is what a server can send in its first round trip. The shaded part is the certificate chain. The ECDSA chain needs less than half the room.

OCSP stapling

One more thing about certificates. Sometimes a certificate has to be cancelled before it runs out, for example when its secret key is stolen. That's called revoking it. How does a browser find out?

The old way was OCSP, the Online Certificate Status Protocol. The browser asked the authority's OCSP server: "Is this certificate still good?" That meant another connection and another wait, before the page.

A server could save its visitors that trip with OCSP stapling. It fetched a fresh, signed "still good" answer itself, and attached it to its handshake. It's like attaching a recent bank letter to a form, so the office doesn't have to phone your bank.

Today, stapling matters much less for speed. The Chromium project says Chrome doesn't generally check OCSP online. It uses its own lists of revoked certificates instead, called CRLSets. And Let's Encrypt, a certificate authority, turned off its OCSP servers on August 6, 2025. Neither of the recorded sites stapled an OCSP answer.

Read this table across: each row is one thing a site can do about its certificates, and why it helps.

Keeping certificates cheap
What to doWhy it helps
Send the intermediates, but not the root.Browsers already have the root, so sending it only adds bytes.
Use ECDSA keys.Their certificates are smaller than RSA ones, so the chain takes less room.
Keep the chain short.Every certificate adds bytes: about 2 KB each in this site's chain.
Don't count on OCSP stapling for speed.Chrome doesn't generally check OCSP online, and Let's Encrypt has turned its OCSP servers off.

HSTS and the redirect it saves

Someone types completefrontend.com with http:// in front, or clicks an old link that starts with http://. What happens?

Plain http:// has no secret code, and this site doesn't send its pages that way. So the server answers with a redirect: a short reply with the status 301, which means "this has moved for good", and the new address, https://completefrontend.com/. Only then does the browser open the real, private connection. The course timed that detour:

The detour through http://Pop out in the code editor (opens in a new tab)JavaScript
// http://completefrontend.com, recorded.const roundTripMs = 23.3;const detourMs = 45.7; // open a line, ask, and get "301: use https://" console.log(`The detour took ${detourMs} ms, ${(detourMs / roundTripMs).toFixed(1)} round trips.`);
  • Line 2 is the same 23.3 ms round trip.
  • Line 3 is the recorded detour: opening a line to http://, asking, and getting the 301 back. It took 45.7 ms.
  • Line 5 prints The detour took 45.7 ms, 2.0 round trips.

That's two whole round trips, and neither brought a single byte of the page. On Ravi's line, it shows even more:

Starting at http:// or at https://Pop out in the code editor (opens in a new tab)JavaScript
const roundTripMs = 150; // Ravi's round tripconst detour = 2; // open a line to http://, ask, and get the redirectconst https = 1 + 1 + 1; // TCP, TLS 1.3, then ask for the page console.log(`From http://, the page starts arriving after ${(detour + https) * roundTripMs} ms.`);console.log(`From https://, it starts arriving after ${https * roundTripMs} ms.`);
  • Line 2 is the detour: two round trips.
  • Line 3 is the real visit: one round trip for TCP, one for TLS 1.3, and one to ask for the page.
  • Line 5 prints From http://, the page starts arriving after 750 ms.
  • Line 6 prints From https://, it starts arriving after 450 ms.

The detour costs Ravi 300 ms before anything useful happens. The picture shows the two visits as round trips:

Two rows of round trips. Going to http:// first: a TCP connection to port 80 and a 301 redirect, then TCP, TLS and the HTML over HTTPS: five round trips. Going straight to https://: TCP, TLS and the HTML: three. The recorded redirect from completefrontend.com took 46 ms.http:// firstTCP301TCPTLSHTMLhttps:// straightTCPTLSHTMLthe detour: 46 ms recorded
The faded boxes are the detour: a whole extra connection before the real one. HSTS, and browsers that switch to https:// on their own, skip it.
Real-life analogyRemembering the side door

The first time you visit a friend's building, the front door is locked, with a sign: "Use the side door." You walk round. Next time, you remember and go straight to the side door, and you save the walk.

In real life: The locked front door, with a sign
In JavaScript: http://, answered with a redirect
In real life: Walking round to the side door
In JavaScript: Opening the real https:// connection
In real life: Going straight to the side door next time
In JavaScript: HSTS: the browser switches to https:// by itself
In real life: How long you trust your memory
In JavaScript: max-age, in seconds

A site can ask the browser to remember. With its https:// pages, it sends a header called Strict-Transport-Security, or HSTS for short. It says: for the next max-age seconds, only ever use https:// for this site. This site sends max-age=31556926, which is about a year.

After one visit, the browser changes http:// to https:// by itself, before anything leaves the computer. There's no detour.

What about the very first visit, before the browser has seen the header? Chrome and Safari now switch every http:// page to https:// on their own, whatever the site says. hstspreload.org explains this. It also runs the HSTS preload list, a list of HTTPS-only sites built into browsers. Because browsers now switch on their own, it says the list adds little, and it no longer recommends joining it.

HSTS itself is still recommended. If an attacker blocks https:// to push the browser back to http://, HSTS makes sure it never falls back. And for browsers that don't switch on their own, it still saves the detour.

Which fix?

Each problem below makes a visit wait, in the handshake or on the way to it. Each has its own fix. Sort them:

Which fix?
  • Every new connection spends two round trips on TLS
  • The server only speaks TLS 1.2
  • A returning visitor's browser checks the whole certificate again
  • Each of a browser's connections to the same host does a full handshake
  • The server sends four certificates, 9 KB, before any of the page
  • An RSA chain fills 40% of the first round trip
  • Old links to http:// go through a redirect on every visit
  • On café Wi-Fi, an attacker could rewrite the http:// redirect and keep a returning visitor on http://
Try it yourself
0 of 8 correct

Sort each problem by the change that fixes it.

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

Common mix-ups

  • "HTTPS is slow." No. With TLS 1.3, a new connection spends one round trip on TLS. A connection that's already open spends none.
  • "Resuming a session always saves a round trip." No. It does only with TLS 1.2. With TLS 1.3, resuming saves bytes and work, and 0-RTT is what saves the round trip.
  • "0-RTT is free speed." No. Someone can record early data and send it again. It's only safe for requests that can be repeated.
  • "The certificate is just a formality." No. It's real bytes in the server's first answer: 6,093 of them for this site's chain.
  • "Every browser checks OCSP on every visit." No. Chrome doesn't generally check online, and Let's Encrypt has turned its OCSP servers off.
  • "HSTS makes the TLS handshake faster." No. The handshake is the same. HSTS skips the http:// detour that comes before it.

Practice exercises

Answer first, then use the hints.

Exercise 1 · Warm-upA satellite link

A satellite internet connection has a 600 ms round trip. Here is the TLS 1.2 example on that line. What does it print?

Starter codePop out in the code editor (opens in a new tab)JavaScript
const roundTripMs = 600; // a satellite link
const tcpTrips = 1; // opening the line
const tlsTrips = 2; // TLS 1.2's handshake

const waitMs = (tcpTrips + tlsTrips) * roundTripMs;
console.log(`With TLS 1.2, the request waits ${waitMs} ms.`);

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

    Exercise 2 · Warm-upA longer chain

    Another site sends a longer RSA chain of 9,100 bytes, so line 2 now holds that. What does the example print?

    Starter codePop out in the code editor (opens in a new tab)JavaScript
    const firstRoundTripBytes = 14600; // what a new connection can send at first
    const chainBytes = 9100; // a longer RSA chain
    
    const share = Math.round((chainBytes / firstRoundTripBytes) * 100);
    console.log(`The certificates fill ${share}% of the first round trip.`);

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

      Exercise 3 · PracticeThe detour with TLS 1.2

      This server only speaks TLS 1.2, so line 3 now counts 1 + 2 + 1. What does the first line print?

      Starter codePop out in the code editor (opens in a new tab)JavaScript
      const roundTripMs = 150; // Ravi's round trip
      const detour = 2; // open a line to http://, ask, and get the redirect
      const https = 1 + 2 + 1; // TCP, TLS 1.2, then ask for the page
      
      console.log(`From http://, the page starts arriving after ${(detour + https) * roundTripMs} ms.`);
      console.log(`From https://, it starts arriving after ${https * roundTripMs} ms.`);

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

        Exercise 4 · PracticeResuming TLS 1.3

        These are the recorded TLS 1.3 handshakes, full and resumed, in the same subtraction as the TLS 1.2 example. What does it print? Did resuming save a round trip?

        Starter codePop out in the code editor (opens in a new tab)JavaScript
        // TLS 1.3 to completefrontend.com, recorded.
        const roundTripMs = 23.3;
        const fullMs = 28.5; // the first handshake
        const resumedMs = 23.8; // the next one, with the ticket
        
        const savedMs = fullMs - resumedMs;
        console.log(`Resuming saved ${savedMs.toFixed(1)} ms, ${(savedMs / roundTripMs).toFixed(1)} round trips.`);

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

          Exercise 5 · ChallengeTLS in DevTools

          In DevTools' Network panel, open the Timing tab of a request that opened a new HTTPS connection. What is the row for the TLS handshake called?

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

            Exercise 6 · ChallengeTwo hosts

            A site serves its HTML from www.example.com and its pictures from img.example.com. The browser opens one connection to each host. On a first visit, how many full TLS handshakes happen?

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

              Check your understanding

              For each question, ask: is this about round trips, bytes, or work?

              The TLS quiz · 7 questionsScore: first tries count
              1. Question 1 of 7What does the TLS 1.3 example print?

                Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
                const roundTripMs = 150; // Ravi's round trip
                const tcpTrips = 1; // opening the line
                const tlsTrips = 1; // TLS 1.3's handshake
                
                const waitMs = (tcpTrips + tlsTrips) * roundTripMs;
                console.log(`With TLS 1.3, the request waits ${waitMs} ms.`);

                Choose an answer to see the explanation.

              2. Question 2 of 7Why does a full TLS 1.3 handshake take one round trip less than TLS 1.2's?

                Choose an answer to see the explanation.

              3. Question 3 of 7On a return visit, what does session resumption let the browser and the server skip?

                Choose an answer to see the explanation.

              4. Question 4 of 7What's the risk of TLS 1.3's 0-RTT early data?

                Choose an answer to see the explanation.

              5. Question 5 of 7What does the chain example print?

                Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
                const firstRoundTripBytes = 14600; // what a new connection can send at first
                const chainBytes = 6093; // the 3 certificates completefrontend.com sends
                
                const share = Math.round((chainBytes / firstRoundTripBytes) * 100);
                console.log(`The certificates fill ${share}% of the first round trip.`);

                Choose an answer to see the explanation.

              6. Question 6 of 7Why does OCSP stapling matter less for speed than it used to?

                Choose an answer to see the explanation.

              7. Question 7 of 7What does HSTS do for speed?

                Choose an answer to see the explanation.

              Key takeaways

              • Before the first request on a new HTTPS connection, the browser and the server agree on a secret code: the TLS handshake.
              • TLS 1.2 spends two round trips on it, and TLS 1.3 spends one. Turn TLS 1.3 on.
              • Resuming a session skips the certificate and the checks. With TLS 1.2, it also saves a round trip.
              • 0-RTT sends the request with the very first message. Use it only for requests that are safe to repeat.
              • The certificate chain travels in the server's first answer. Keep it short, leave out the root, and prefer ECDSA.
              • OCSP stapling matters less now, because Chrome doesn't generally check OCSP online.
              • A detour through http:// costs about two round trips. HSTS, and browsers that switch to https:// on their own, skip it.

              Remember the one-liner.
              One round trip to agree on the code, none on a return visit with 0-RTT, and never a detour through http://.

              Sources

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

              Coming next: HTTP/1.1 and HTTP/2. The line is open and private. Now, how many requests can it carry at once? The next lesson goes from six connections per host to one shared connection, and which old tricks to stop using.

              CompleteFrontend Clear concepts. Working examples.