Every HTTPS request starts with a conversation most engineers never look at. Before your browser sends GET /, it and the server agree on a protocol version, exchange keys, check a certificate and prove that nobody tampered with any of it. When that goes wrong you get cryptic errors such as handshake_failure or bad_certificate, and when it goes slowly every page pays for it.
TLS 1.3 made the handshake shorter and more private than TLS 1.2. This post walks through it message by message with a sequence diagram, then covers the variations you meet in production: HelloRetryRequest, session resumption and 0-RTT, mutual TLS, Encrypted Client Hello and post-quantum key exchange.
What the handshake has to achieve
A TLS handshake does four jobs before any application data flows:
- Negotiate parameters: the protocol version, the cipher suite and the key exchange group.
- Establish shared keys: both sides derive the same secrets without ever sending them.
- Authenticate the server, and optionally the client, with certificates.
- Confirm integrity: both sides check that they saw the same handshake messages, so an attacker cannot downgrade or alter the negotiation.
The current specification is RFC 9846, published in July 2026. It replaces RFC 8446 but keeps the same protocol version, so a TLS 1.3 client built against the old RFC still talks to a server built against the new one.
The full handshake, message by message
TLS 1.3 needs one round trip. The client guesses which key exchange the server will accept and sends its half of the key exchange in the very first message.
| Message | Direction | Encrypted | What it carries |
|---|---|---|---|
| ClientHello | client → server | No | Random value, cipher suites, supported_versions, key_share, signature_algorithms, server_name (SNI), ALPN |
| ServerHello | server → client | No | Chosen cipher suite, the server's key_share |
| EncryptedExtensions | server → client | Yes | Remaining negotiated extensions, such as the ALPN protocol |
| Certificate | server → client | Yes | The server's certificate chain |
| CertificateVerify | server → client | Yes | A signature over the handshake so far, made with the certificate's private key |
| Finished | server → client | Yes | A MAC over the whole transcript |
| Finished | client → server | Yes | The client's MAC over the transcript |
The important line is the one after ServerHello. Once the client has the server's key share, both sides run the key schedule (HKDF over the shared secret and the transcript) and derive handshake traffic keys. From that point on, every handshake message is encrypted. In TLS 1.2 an observer on the network could read the server's certificate; in TLS 1.3 they cannot.
CertificateVerify proves that the server holds the private key for the certificate it just sent. Finished proves that both sides saw the same messages. If an attacker stripped a cipher suite from the ClientHello, the transcripts would differ and the Finished check would fail.
The server can send application data right after its Finished, and the client sends its first request immediately after its own Finished. One round trip after the ClientHello, the encrypted connection is ready.
What changed from TLS 1.2
| TLS 1.2 | TLS 1.3 | |
|---|---|---|
| Round trips for a new connection | 2 | 1 |
| Key exchange | RSA key transport or (EC)DHE | (EC)DHE only, or PSK with (EC)DHE |
| Forward secrecy | Optional | Always, except PSK-only resumption |
| Cipher suites | Dozens, including CBC and RC4 | Five, all AEAD |
| Server certificate on the wire | In clear text | Encrypted |
| Compression and renegotiation | Supported | Removed |
The cipher suite list shrank to TLS_AES_128_GCM_SHA256, TLS_AES_256_GCM_SHA384, TLS_CHACHA20_POLY1305_SHA256 and two CCM suites. A TLS 1.3 cipher suite now names only the record encryption and the hash; the key exchange and the signature algorithm are negotiated separately. RSA certificates still work. What disappeared is RSA key transport, where the client encrypted the secret with the server's public key and a stolen key decrypted every recorded session.
When one round trip becomes two: HelloRetryRequest
The one-round-trip handshake depends on a guess. The client sends key shares for the groups it expects the server to accept, usually X25519. If the server supports none of them, it replies with a HelloRetryRequest naming a group it does accept, and the client starts again with a new ClientHello. That costs a full extra round trip.
You see this when a client and a server disagree about groups, for example a server configured for P-256 only and a client that sends only an X25519 share. The fix is configuration: make sure the groups your clients offer first are the ones your servers prefer.
Resumption and 0-RTT
After the handshake, the server can send NewSessionTicket messages. A ticket lets the client resume later with a pre-shared key (PSK) instead of a full certificate exchange. Resumption normally still runs a fresh (EC)DHE exchange, so the resumed session keeps forward secrecy.
With a ticket, the client can also send early data, called 0-RTT: the first request travels in the same flight as the ClientHello. It saves a round trip, but RFC 9846 is explicit that 0-RTT data has no guarantee against replay between connections. An attacker who captures the first flight can send it again.
The practical rules:
- Send only idempotent requests as early data:
GETwithout side effects, never a payment or an order. - On the server, enable 0-RTT only where the application can reject or tolerate replays. Many servers mark early-data requests (for example with the
Early-Data: 1header defined in RFC 8470), so the application can answer425 Too Early. - Do not use 0-RTT for authenticated actions unless you have thought through replay.
Mutual TLS
In mutual TLS (mTLS) the server also asks the client for a certificate. The server adds a CertificateRequest after EncryptedExtensions, and the client answers with its own Certificate and CertificateVerify before its Finished. The round-trip count does not change.
mTLS is the default between services in a mesh: every workload gets a short-lived certificate, and sidecars or proxies handle the handshake. The service mesh architecture diagram shows where those certificates come from, and the zero trust architecture diagram explains why identity per request matters more than the network a request came from.
Privacy and post-quantum keys: what is new in 2026
Two recent RFCs change what the handshake looks like on the wire.
Encrypted Client Hello. The ClientHello is sent before any keys exist, so the SNI, the host name the client wants, travels in clear text. RFC 9849, published in March 2026, defines Encrypted Client Hello (ECH): the client encrypts an inner ClientHello, including the real host name, with a key the server publishes in DNS. Observers see only a public outer name, typically that of a large provider.
Hybrid post-quantum key exchange. Traffic recorded today could be decrypted later by a quantum computer, so key exchange is moving first. RFC 10024, published in August 2026, defines hybrids such as X25519MLKEM768, which combines X25519 with ML-KEM-768. Both shares feed the key schedule, so the connection stays secure as long as either algorithm holds. Chrome and Edge 131 and later and Firefox 132 and later offer it by default.
The cost is size. An X25519MLKEM768 client share is 1,216 bytes and the server share 1,120 bytes, against 32 bytes each for plain X25519. A ClientHello can now span two packets, which exposed middleboxes that assumed it never would.
The cost in round trips
Round trips, not bandwidth, decide how quickly a connection is ready. With a 100 ms round trip between client and server, ignoring DNS:
| Setup | Round trips before the response | Time |
|---|---|---|
| TCP + TLS 1.2 + request | 4 | 400 ms |
| TCP + TLS 1.3 + request | 3 | 300 ms |
| TCP + TLS 1.3 with 0-RTT | 2 | 200 ms |
| QUIC (HTTP/3), new connection | 2 | 200 ms |
| QUIC with 0-RTT | 1 | 100 ms |
QUIC carries the same TLS 1.3 handshake inside its own packets (RFC 9001) and merges it with the transport handshake, which is where the extra round trip goes. Terminating TLS close to users, at a CDN or an edge load balancer, shortens the round trip itself; the load balancer diagram covers where termination usually happens.
The handshake diagram
Diagram contents
- Client (browser)
- Server
- mutual TLS only
- ClientHello: key_share, supported_versions, SNI, ALPN
- ServerHello: key_share, chosen cipher suite
- {EncryptedExtensions}
- {CertificateRequest} (mTLS only)
- {Certificate}: server chain
- {CertificateVerify}: signature over the transcript
- {Finished}: MAC over the transcript
- {Certificate}: client chain
- {CertificateVerify}
- {Finished}
- [Application data], both directions
- NewSessionTicket (for resumption)
How to read it:
- Two lifelines. The client on the left, the server on the right. Time runs downward.
- Plain arrows are sent in clear text: ClientHello and ServerHello. Everything below the line marked "handshake keys" is encrypted.
- The server's flight carries EncryptedExtensions, Certificate, CertificateVerify and Finished in one go, which is why the handshake fits in one round trip.
- The optional fragment holds the client's Certificate and CertificateVerify, sent only in mutual TLS. The dashed CertificateRequest in the server's flight is what asks for them.
- The Markdown block lists the extensions that decide whether the handshake succeeds: versions, groups, signature algorithms, SNI and ALPN.
Drawing protocol diagrams that people can debug with
- Mark the encryption boundary. The most common question about TLS is what an observer can see. Draw the line.
- Name the extensions that matter, not all of them.
key_shareandsupported_versionsexplain most failures. - Put optional and alternative paths in fragments. An
optfragment for client certificates and analtfragment for HelloRetryRequest keep the main flow readable. - Keep one diagram per question. A full handshake, a resumption with 0-RTT and an mTLS exchange are three diagrams if each needs detail.
The sequence diagram example covers lifelines, fragments and naming conventions in more depth.
Build it in Archboard
- Sign in with Google or GitHub and create a diagram.
- Add two Lifeline nodes from the Sequence pack, named Client and Server.
- Draw the messages as edges between the lifelines, top to bottom: ClientHello, ServerHello, then the four encrypted server messages and the client's Finished. Label each edge with the message name and its key fields.
- Add a Fragment from the Sequence pack with the operator
optaround the client's Certificate and CertificateVerify, and draw the server's CertificateRequest as a dashed edge. - Add a Markdown block from the Core pack with the extensions and the round-trip count, and a Note marking where encryption starts.
The example uses 4 diagram nodes, within the Free plan.
FAQ
How many round trips does a TLS 1.3 handshake take? One for a new connection, compared with two in TLS 1.2. A HelloRetryRequest adds a second, and 0-RTT resumption lets the client send its first request with no handshake round trip at all.
Is the server certificate encrypted in TLS 1.3? Yes. Everything after ServerHello, including the Certificate message, is encrypted with handshake keys. The ClientHello is not, which is the gap Encrypted Client Hello closes.
Is 0-RTT safe to enable? Only for requests that are safe to replay. Early data can be captured and sent again, so restrict it to idempotent requests and let the application reject anything else with 425 Too Early.
Does TLS 1.3 still support RSA certificates? Yes. RSA signatures are still used to authenticate servers. TLS 1.3 removed RSA key transport, where the session secret was encrypted with the server's RSA key and a leaked key exposed past traffic.
What is the difference between TLS and mTLS? In regular TLS only the server presents a certificate. In mutual TLS the server requests a certificate from the client too, so both sides are authenticated in the same handshake.