Blog
5 min read

The TLS 1.3 Handshake Explained: What Happens Before the First Byte

What actually happens when a browser connects over HTTPS: ClientHello, key shares, the server's certificate and signature, Finished messages, one round trip instead of two, 0-RTT resumption and its replay risk, SNI and ECH, certificate chain validation, and how to inspect it all with openssl.

Every HTTPS request starts with a TLS handshake: client and server agree on algorithms, the server proves its identity, and both derive keys that nobody watching the network can compute. TLS 1.3 (2018) redesigned this to be faster and to remove a decade of insecure options. (What is HTTPS? covers the beginner version.)

The goals

  1. Confidentiality — eavesdroppers can't read the traffic.
  2. Integrity — they can't modify it undetected.
  3. Authentication — the client knows it's talking to the real example.com (and optionally vice versa).
  4. Forward secrecy — stealing the server's private key later doesn't decrypt past recordings.

The full handshake: one round trip

Client                                           Server
ClientHello
  + supported versions, cipher suites
  + key_share (ephemeral ECDHE public key)
  + server_name (SNI), ALPN (h2, http/1.1)
                         ───────────▶
                                                 ServerHello
                                                   + chosen cipher suite
                                                   + key_share (its ECDHE public key)
                                    ── now both derive handshake keys ──
                                                 {EncryptedExtensions}  (ALPN result…)
                                                 {Certificate}
                                                 {CertificateVerify}    (signature)
                                                 {Finished}
                         ◀───────────
{Finished}
[Application data — e.g. GET /]  ───────────▶

{…} = encrypted. Step by step:

ClientHello. The client lists what it supports and — the key TLS 1.3 change — guesses the key exchange group (usually X25519, increasingly a hybrid post-quantum group like X25519MLKEM768) and sends its ephemeral public key straight away. It also sends SNI (which hostname it wants, so one IP can serve many certificates) and ALPN (which application protocol, e.g. h2).

ServerHello. The server picks a cipher suite and sends its own ephemeral public key. Now both sides compute the same shared secret via (EC)DHE, and derive handshake keys with HKDF. Everything after this point is encrypted.

EncryptedExtensions. Remaining negotiated parameters, like the chosen ALPN protocol.

Certificate + CertificateVerify. The server sends its certificate chain, then signs a hash of the whole handshake so far with the certificate's private key. That signature proves it holds the key for the certificate and binds this specific exchange, preventing downgrades and splicing.

Finished. Each side sends a MAC over the full transcript using keys derived from the shared secret. If anything was tampered with, verification fails.

The client can send application data immediately after its Finished: one round trip (1-RTT) before the request, versus two in TLS 1.2. If the server doesn't support the client's guessed group, it replies with a HelloRetryRequest and the handshake costs an extra round trip.

What the client checks in the certificate

Validation happens before the client trusts anything:

  • The chain links the leaf certificate to a trusted root via intermediates (servers must send the intermediates — a missing one is a classic mobile-only failure). (Your connection is not private)
  • The hostname matches a Subject Alternative Name (wildcards cover one label only).
  • Dates are valid — and lifetimes are shrinking (Let's Encrypt's are heading towards 45 days by 2028). (How automatic SSL works)
  • Not revoked — in practice browsers increasingly rely on their own revocation lists, and public CAs publish to Certificate Transparency logs, which browsers require.

What TLS 1.3 removed

Static RSA key exchange (no forward secrecy), CBC-mode ciphers, RC4, SHA-1, compression, renegotiation, and custom DH groups — the sources of attacks like BEAST, CRIME, Lucky13, Logjam and ROBOT. Only five AEAD cipher suites remain (AES-GCM and ChaCha20-Poly1305 variants), and every key exchange is ephemeral, so forward secrecy is mandatory.

Resumption and 0-RTT

After a handshake, the server can issue a session ticket (a pre-shared key). On reconnection the client presents it and both skip certificate verification — still 1-RTT, but cheaper.

With 0-RTT (early data), the client sends its HTTP request in the first flight, encrypted with the resumption key. Zero round trips — but early data has no replay protection: an attacker can capture and resend it. So:

  • Only allow 0-RTT for idempotent requests (GETs without side effects). (GET vs POST)
  • Servers and CDNs typically mark it (e.g. an Early-Data: 1 header) so the app can reject or defer unsafe requests (HTTP 425 Too Early).

Privacy: SNI and Encrypted Client Hello

Even in TLS 1.3, the ClientHello — including SNI — is sent in plaintext, so networks can see which site you're visiting. Encrypted Client Hello (ECH) encrypts the inner ClientHello using a key published in DNS (HTTPS/SVCB records), leaving only a generic outer name visible. Support is growing across browsers and CDNs.

Mutual TLS

The server can send a CertificateRequest; the client then sends its own Certificate and CertificateVerify. That's mTLS, used for service-to-service authentication. (API authentication methods)

Inspect it yourself

openssl s_client -connect example.com:443 -servername example.com -tls1_3 -brief
# Protocol version: TLSv1.3
# Ciphersuite: TLS_AES_256_GCM_SHA384
# Peer certificate: CN = example.com
# Negotiated TLS1.3 group: X25519MLKEM768
# Verification: OK

openssl s_client -connect example.com:443 -servername example.com -showcerts </dev/null
curl -v https://example.com 2>&1 | grep -E 'SSL|TLS|ALPN'

In Wireshark, set SSLKEYLOGFILE in your browser's environment to decrypt your own sessions for debugging.

Where TLS terminates in your stack

Usually at the edge: a CDN, load balancer or reverse proxy holds the certificate and decrypts; traffic to your app may be plain HTTP on a private network or re-encrypted. Know where each hop is encrypted, and pass the original scheme via X-Forwarded-Proto. (Reverse proxies explained, Cloudflare proxied vs DNS only)

The summary

  • TLS 1.3 does key exchange in the first message: one round trip to encrypted data.
  • The server proves identity by signing the handshake transcript with its certificate key.
  • All key exchange is ephemeral — forward secrecy by default; legacy ciphers are gone.
  • 0-RTT is fast but replayable — idempotent requests only.
  • SNI leaks the hostname unless ECH is used; openssl s_client shows what was negotiated.

EasySpawn terminates TLS for your custom domains with automatically renewed certificates and modern protocol settings, so your app gets HTTPS without managing any of this. See how it works or join the waitlist.

Related: What Is HTTPS? · How Automatic SSL Works · HTTP/2 vs HTTP/3 · Encryption at Rest vs in Transit

Keep reading