Before your browser sends the first byte of an HTTP request, it has to perform a small ritual with the server.
Not a huge ritual anymore. TLS 1.3 made sure of that. But it is still a real exchange of promises, proofs, and key material, and it all happens in 50-100 milliseconds — quickly enough that most developers never think about it. They type https://, the page loads, and the handshake disappears under the paint.
It shouldn’t disappear. It is one of the best pieces of engineering in the modern web stack.
Step 1: ClientHello
The client opens by laying its cards on the table.
The ClientHello message contains: the TLS versions the browser supports, a list of cipher suites (named combinations of key exchange, encryption, and hash algorithms), a random 32-byte value, the server name via SNI, and — most importantly — a key_share.
This is the big speed change from older handshakes. In TLS 1.2, the browser had to wait for the server to pick a key exchange algorithm before generating key material. TLS 1.3 doesn’t wait. The client makes an educated guess about which algorithm the server will choose (usually X25519), generates ephemeral key material, and includes it upfront.
That one decision is why TLS 1.3 feels faster. The protocol stopped being polite about waiting.
Step 2: ServerHello
The server picks from the client’s offerings. It selects a cipher suite, responds with its own random value, and — if it agrees with the client’s key exchange guess — includes its own key_share.
At this point both sides have exchanged random values and key shares. They can each independently derive the same shared secret using Diffie-Hellman. The handshake encryption keys are computed. Everything from here on is encrypted.
In TLS 1.2, this took two round-trips. TLS 1.3 collapses it into one.
Step 3: Server Certificate
The server sends its certificate chain — the leaf certificate for the domain, any intermediate certificates, and implicitly the root CA that signed them. The chain proves the server’s identity: “I am example.com, and here’s the cryptographic proof signed by a trusted authority.”
The browser verifies the chain against its root store — the list of ~150 certificate authorities it trusts. If any link in the chain is broken, expired, revoked, or untrusted, the handshake fails.
This is where things go wrong most often in practice. Expired certificates, missing intermediates, hostname mismatches. The cryptography is fine. The PKI housekeeping is where humans make mistakes.
Step 4: CertificateVerify
The server proves it actually holds the private key for the certificate. It signs a hash of the entire handshake transcript so far — every message exchanged up to this point — with its private key.
This is what prevents someone from just copying a certificate and pretending to be the server. The certificate is public. The private key is not. Only the real server can produce this signature.
Step 5: Server Finished
The server sends a Finished message containing a MAC (Message Authentication Code) over the full handshake transcript. This proves the server saw the same messages the client sent and that nothing was tampered with in transit.
At this point the server is done talking about the handshake. It’s ready for application data.
Step 6: Client Verification
The client’s turn. It verifies the certificate chain against its root store. It checks that the hostname matches the certificate’s subject (or SAN entries). It verifies the CertificateVerify signature against the certificate’s public key. It confirms the Finished MAC is valid.
If anything fails — wrong hostname, expired cert, bad signature, untrusted CA — the browser kills the connection and shows an error page. There’s no partial trust. It’s all or nothing.
The client also derives the application traffic keys at this point, using the shared secret from the Diffie-Hellman exchange plus the handshake transcript.
Step 7: Client Finished
The client sends its own Finished message — another MAC over the handshake transcript from the client’s perspective. Handshake complete. Both sides now have identical symmetric keys derived from the shared secret.
Application data flows. Your HTTP request — the GET, the POST, the headers, the cookies — travels through the encrypted channel. The handshake is over. The invisible negotiation that took 50 milliseconds is done.
Why TLS 1.3 Is Faster
TLS 1.2 needed two full round-trips before application data could flow. TLS 1.3 needs one. The speculative key share in ClientHello is the trick — by guessing the server’s preferred algorithm, the client saves an entire round-trip.
There’s even a 0-RTT resumption mode for repeat connections. The client caches a session ticket from a previous handshake and includes early application data in the very first message. The server can process it immediately. Zero round-trips before data flows.
The catch: 0-RTT data can be replayed. An attacker who captures the first message can resend it. This means 0-RTT should only carry idempotent requests — GETs, not POSTs. The speed comes with a tradeoff, and not everyone should use it.
What Goes Wrong
At each step, things can break. Certificate expired — the most common failure, usually because someone forgot to renew. Hostname mismatch — the cert says www.example.com but you connected to example.com. Missing intermediate — the server only sends the leaf cert and the client can’t build the chain. Clock skew — the client’s system clock is wrong, so a valid certificate looks expired. Unsupported cipher — the server and client share no common cipher suite, usually because one side is outdated.
Most TLS failures aren’t cryptographic. They’re operational. The math works. The humans maintaining the certificates are the fragile part.