Every HTTPS request starts with a TLS handshake — a brief negotiation in which the client and server agree on which cipher to use, exchange keys, and establish an encrypted tunnel. It happens in milliseconds and most developers never look at it. But understanding it is essential for diagnosing connection problems, optimizing latency, and reasoning about web security in general.
This post walks through the TLS handshake at a practical level: what TLS 1.2 looked like, what TLS 1.3 does differently, what each message contains, and how it all combines into the fast, secure connection you take for granted in 2026.
A Quick Refresher on TLS
TLS (Transport Layer Security) is the protocol that adds encryption, authentication, and integrity to TCP connections. It’s the successor to SSL (the name “SSL” is colloquially still used but the actual protocol has been TLS since 1999).
A TLS connection accomplishes:
- Authentication — verifies you’re really talking to the server you intended (via certificates).
- Encryption — protects the payload from on-path observers.
- Integrity — prevents tampering with messages in transit.
TLS 1.2 was released in 2008 and dominated for a decade. TLS 1.3, released in 2018, is the modern default. By 2026, TLS 1.3 is what virtually all modern clients and servers negotiate.
The TLS 1.2 Handshake (For Context)
TLS 1.2 needs two round trips before any application data can flow:
1. Client → Server: ClientHello
(supported versions, cipher suites, random nonce, extensions)
2. Server → Client: ServerHello + Certificate + ServerKeyExchange + ServerHelloDone
(chosen version, chosen cipher, server's certificate, key-exchange params)
3. Client → Server: ClientKeyExchange + ChangeCipherSpec + Finished
(client's key-exchange params, "now switching to encrypted mode")
4. Server → Client: ChangeCipherSpec + Finished
(server's "now switching to encrypted mode")
5. Application data starts flowing.
Each step adds a network RTT. Setting up a TLS 1.2 connection adds ~100-200ms on top of the TCP connection establishment.
The TLS 1.3 Handshake
TLS 1.3 cuts this to one round trip:
1. Client → Server: ClientHello
(supported versions, cipher suites, random nonce, KeyShare for ECDHE, extensions)
2. Server → Client: ServerHello + EncryptedExtensions + Certificate + Finished
(chosen version, chosen cipher, KeyShare, server's certificate — all encrypted from this message onward)
3. Client → Server: Finished
4. Application data starts flowing.
The big change: the client sends its key-exchange parameters in the first message (the ClientHello). The server can derive the shared secret immediately and respond with everything else, including the encrypted half of the handshake, in one round trip.
For users, this means TLS 1.3 saves ~50-100ms per new connection compared to TLS 1.2.
What Each Field Means
Cipher suite
Specifies the algorithms used: key exchange, authentication, encryption, hash. TLS 1.3 simplified the cipher suite to specify only the symmetric cipher (e.g., TLS_AES_256_GCM_SHA384) — key exchange and signature algorithms are negotiated separately.
Random nonce
A random value from each side, used in key derivation. Prevents replay attacks.
KeyShare
TLS 1.3’s mechanism for ECDHE key exchange. Each side sends its public key for an elliptic curve Diffie-Hellman exchange. Both sides can then derive the same shared secret without ever transmitting it.
Certificate
The server’s identity. A chain of X.509 certificates ending at a CA that the client trusts. The client verifies:
- The certificate is for the hostname being connected to (matches SNI / certificate Common Name / SAN).
- The certificate chain leads to a trusted root CA.
- The certificate hasn’t been revoked (OCSP / CRL).
- The certificate hasn’t expired.
Finished
A MAC over the entire handshake transcript so far. Confirms that both sides saw the same handshake messages (preventing downgrade attacks).
Session Resumption: 0-RTT
TLS 1.3 supports 0-RTT session resumption. If the client has previously connected to the server, it can include encrypted data in the very first message — application data starts flowing in 0 RTT.
1. Client → Server: ClientHello + EarlyData (encrypted with previous session key)
2. Server → Client: ServerHello + Finished + EarlyDataResponse
3. Application data continues flowing.
Use cases: API calls where the request is small and idempotent, GET requests where the cost of replay is acceptable.
Caveat: 0-RTT data is replayable. An attacker who captures the encrypted EarlyData blob could replay it later. For non-idempotent operations (transactions, writes), don’t use 0-RTT.
SNI: How the Server Knows Which Cert to Serve
One server IP, many domains. How does the server know which certificate to present?
Server Name Indication (SNI) — an extension in the ClientHello that tells the server which hostname the client wants:
ClientHello {
extensions: [
server_name: "example.com"
]
}
The server uses this to pick the right certificate. Without SNI, a server hosting multiple domains couldn’t serve TLS properly.
SNI is sent in plaintext in the ClientHello (because the server needs it before encryption is set up). This means on-path observers can see which domain you’re connecting to even though the actual data is encrypted.
Encrypted Client Hello (ECH) is the 2026-current solution: it encrypts the SNI using a public key advertised in DNS. Most major sites and CDNs support ECH; it’s a meaningful privacy improvement.
Certificate Authorities and Trust
The whole TLS authentication model rests on certificate authorities (CAs). Browsers and OSes ship with a list of trusted root CAs (a few hundred). When a server presents a certificate, the client checks that the certificate chain leads to one of these roots.
If you’ve ever seen “your connection is not private” — that’s a chain validation failure. The certificate might be:
- Expired
- Issued for the wrong hostname
- Self-signed (no CA in chain)
- Issued by a CA the client doesn’t trust
- Revoked
In 2026 the dominant free CA is Let’s Encrypt. Commercial CAs (DigiCert, Sectigo, etc.) still exist for EV certificates and specific compliance requirements, but Let’s Encrypt dominates by volume.
What “TLS” Encrypts and Doesn’t
A common confusion: TLS protects the application data. It doesn’t hide everything.
Encrypted by TLS:
- HTTP request methods, URLs, headers, bodies
- HTTP response status codes, headers, bodies
- All application data within the TLS connection
Visible to on-path observers:
- The destination IP (visible in the IP header)
- The destination port (in the TCP header)
- The SNI field in ClientHello (unless ECH is in use)
- The size and timing of encrypted packets (traffic-analysis observable)
- DNS queries (unless DoH/DoT is in use)
The full privacy stack (TLS + DoH + ECH + VPN) closes most of these gaps. Pure TLS closes the application-data gap.
TLS Performance
For an empty TLS 1.3 connection to a previously-unseen server:
- TCP handshake: 1 RTT
- TLS 1.3 handshake: 1 RTT
- HTTP request: 1 RTT
- Total: 3 RTTs before useful data
For QUIC (HTTP/3, see HTTP/2 vs HTTP/3) over UDP:
- QUIC + TLS 1.3 handshake combined: 1 RTT
- HTTP request: 1 RTT
- Total: 2 RTTs
QUIC eliminates the separate TCP handshake by integrating transport and TLS into one protocol. This is one of the main reasons HTTP/3 is faster than HTTP/2 for first-request latency.
Diagnosing TLS Problems
A few practical tips when TLS misbehaves:
openssl s_client
The first-resort tool:
openssl s_client -connect example.com:443 -servername example.com
Shows the certificate chain, negotiated cipher, TLS version. Lets you read the certificate fields by hand.
curl --verbose
curl -v https://example.com 2>&1 | grep '^\*'
Shows the TLS negotiation steps inline with the HTTP request.
Wireshark
For deep diagnosis. The ClientHello and ServerHello are plaintext and visible. You can see exactly what was negotiated, what extensions were sent, etc.
Common errors
- “unable to verify the first certificate” — Missing intermediate cert. Server must send the full chain, not just the leaf.
- “hostname mismatch” — Cert doesn’t include the domain you’re connecting to in its SAN list.
- “certificate has expired” — Renew via Let’s Encrypt (or your CA).
- “handshake failure” — Usually cipher mismatch between client and server. Modern clients drop SSLv3, TLS 1.0, TLS 1.1; servers stuck on those won’t negotiate.
TLS in Your Application
For most application developers, TLS is handled by:
- The reverse proxy / load balancer (nginx, HAProxy, AWS ALB) which terminates TLS and forwards plaintext to your backend over the internal network.
- The CDN (Cloudflare, CloudFront) which terminates TLS at the edge.
- The cloud platform (Vercel, Heroku, Render) which manages certs automatically.
You almost never write TLS code yourself in 2026. Where it matters:
- Choose modern defaults. TLS 1.3 only, modern ciphers, HSTS enabled.
- Manage certificates with automation (Let’s Encrypt + cert-manager / Caddy / ACME client).
- Don’t pin certificates unless you have a clear threat model — pinning breaks when you renew.
- Test with SSL Labs (
ssllabs.com/ssltest) periodically.
TLS and HTTP/3
HTTP/3 doesn’t use TCP at all — it runs over QUIC, which has TLS 1.3 baked in. There’s no separate TLS handshake; the QUIC handshake is the TLS handshake. See HTTP/2 vs HTTP/3 for the broader transport changes.
TL;DR
- TLS handshake sets up an encrypted channel before HTTP (or other application data) can flow.
- TLS 1.2 needs 2 RTTs. TLS 1.3 needs 1 RTT.
- TLS 1.3 + 0-RTT can resume previous sessions in 0 RTTs (for replayable requests only).
- SNI is plaintext; ECH encrypts it.
- TLS encrypts application data but not the destination IP/port, packet timing, or pre-ECH SNI.
- Cert chain validation is the main reason connections fail.
- In your applications, TLS is handled by a reverse proxy or CDN; choose modern defaults and automate certs.
The TLS handshake is a small but critical part of every secure connection. Once you’ve internalized the 1.3 flow (ClientHello → ServerHello+Finished → Finished → data), most production TLS debugging becomes straightforward. For the related encrypted-DNS layer, see DNS over HTTPS vs TLS; for the transport that wraps TLS in 2026, see HTTP/2 vs HTTP/3.