How HTTPS Works: The TLS 1.3 Handshake Explained
Every HTTPS connection begins with a handshake: a short negotiation that authenticates the server, agrees on cryptographic keys, and does it without ever sending those keys across the wire. TLS 1.3 (RFC 8446) streamlined this process dramatically compared to TLS 1.2, cutting a full network round trip and removing decades of accumulated cryptographic baggage. This article walks through what actually happens, byte by byte at a conceptual level, when your client connects to a server.
The big picture: one round trip
The core insight of TLS 1.3 is that the client can guess what the server will accept and send its key material immediately, rather than waiting for the server to choose first. A full TLS 1.3 handshake looks like this:
Client Server
------ ------
ClientHello
+ key_share (ECDHE public key)
+ supported_versions
+ signature_algorithms
+ server_name (SNI)
+ application_layer_protocol (ALPN) -------->
ServerHello
+ key_share (ECDHE public key)
{EncryptedExtensions}
{Certificate}
{CertificateVerify}
{Finished}
<--------
{Finished} -------->
[Application Data] <-------> [Application Data]
Braces { } denote messages encrypted under handshake keys; brackets [ ] denote messages encrypted under the final application keys. Notice that everything after ServerHello is already encrypted. This is the 1-RTT handshake: the client can send real application data after just one round trip.
ClientHello: the opening offer
The client sends a single plaintext message containing everything the server needs to proceed. The important fields:
- supported_versions — In TLS 1.3 the legacy
versionfield is frozen at0x0303(TLS 1.2) for middlebox compatibility; the real version negotiation happens here. This extension is what actually signals "I speak 1.3." - cipher_suites — A short list. TLS 1.3 only defines five AEAD suites (e.g.
TLS_AES_128_GCM_SHA256), and crucially they decouple the AEAD cipher and hash from the key-exchange and signature algorithms, which are negotiated separately. - supported_groups and key_share — The client names the elliptic-curve / finite-field groups it supports (e.g.
x25519,secp256r1) and, optimistically, includes one or more ephemeral public keys so the server can complete key exchange in the same flight. - signature_algorithms — Which signature schemes the client will accept for the server's certificate.
- server_name (SNI) and application_layer_protocol_negotiation (ALPN) — covered below.
ECDHE and forward secrecy
TLS 1.3 mandates ephemeral key exchange. Both sides perform an Ephemeral Elliptic Curve Diffie-Hellman (ECDHE) exchange: each generates a fresh key pair for this connection only, exchanges public keys, and independently computes the same shared secret.
The "ephemeral" part is the whole point. Because the private keys are discarded after the handshake, an attacker who records the encrypted traffic today and later steals the server's long-term private key still cannot decrypt that traffic. This property is called forward secrecy, and TLS 1.3 removed the old static-RSA key exchange precisely because it lacked it. The shared ECDHE secret is never transmitted; it is derived on both ends.
That raw secret is then run through HKDF (HMAC-based Key Derivation Function) in a structured "key schedule" that mixes in transcript hashes to produce distinct keys for the handshake phase and the application phase. Binding keys to the running transcript hash means any tampering with earlier messages invalidates later ones.
ServerHello and certificates
The server picks one group and one cipher suite, returns its own key_share in the ServerHello, and from that point on both sides can compute the handshake keys. Everything the server sends next is encrypted:
- EncryptedExtensions — extensions that don't affect key agreement (including the negotiated ALPN protocol), now hidden from passive observers.
- Certificate — the server's certificate plus the intermediate CA certificates needed to build a chain.
- CertificateVerify — a digital signature over the entire handshake transcript so far, produced with the private key matching the certificate's public key. This proves the server actually possesses the private key and isn't just replaying someone else's certificate.
- Finished — an HMAC over the transcript that confirms the handshake wasn't tampered with.
The chain of trust
A certificate alone proves nothing. Authentication works because the server's certificate is signed by an intermediate CA, whose certificate is signed (directly or through more intermediates) by a root CA whose certificate ships in your operating system or browser trust store. The client validates the chain by:
- Building a path from the leaf certificate up to a trusted root.
- Verifying each signature in the chain with the issuer's public key.
- Checking validity dates, that the leaf's Subject Alternative Name matches the requested hostname, key usage constraints, and revocation status (CRL/OCSP).
You can inspect a live chain with OpenSSL:
openssl s_client -connect example.com:443 -servername example.com -tls1_3 </dev/null \
| openssl x509 -noout -text
SNI: which site are you asking for?
A single IP address often hosts many domains. Server Name Indication is the ClientHello extension that tells the server which hostname the client wants before the certificate is selected, so the server can present the correct certificate. The privacy weakness is that classic SNI is sent in plaintext, leaking the destination hostname to anyone watching. Encrypted Client Hello (ECH) is the evolving solution, encrypting the sensitive ClientHello fields under a public key the server publishes via DNS.
ALPN: choosing the application protocol
Application-Layer Protocol Negotiation lets the client and server agree on what runs on top of TLS without an extra round trip. The client offers an ordered list (e.g. ["h2", "http/1.1"]) and the server picks one, returning it in EncryptedExtensions. This is how a browser and server settle on HTTP/2 versus HTTP/1.1, and it is mandatory for negotiating HTTP/2 over TLS.
Session resumption and 0-RTT
After a successful handshake, the server can issue one or more session tickets (NewSessionTicket messages). On a later connection the client presents a ticket via a pre-shared key (PSK), letting both sides skip certificate exchange and resume from previously established keying material. This is faster and cheaper.
TLS 1.3 goes further with 0-RTT (zero round trip time): when resuming, the client can send application data in its very first flight, encrypted with keys derived from the PSK, before the server has responded. The latency win is real, but it comes with two serious caveats developers must understand:
- No forward secrecy for the 0-RTT data. That early data is protected only by the PSK, not by a fresh ECDHE exchange, so it lacks the forward-secrecy guarantee of the rest of the connection.
- Replay attacks. Because 0-RTT data is sent before any live server interaction, a network attacker can capture and replay it. The server cannot cryptographically distinguish a replay from the original.
The practical rule: only send idempotent requests as 0-RTT early data. A GET that reads a public resource is fine; a POST that transfers money or mutates state is not. Servers should reject early data for non-idempotent endpoints and apply anti-replay measures (such as single-use tickets or a strike register). Most HTTP stacks expose a flag indicating a request arrived as early data so you can route it accordingly.
Practical takeaway
TLS 1.3 gives you authenticated, encrypted, forward-secret connections in a single round trip, with a deliberately small set of strong algorithms that remove most of the foot-guns of earlier versions. As a developer you rarely write handshake code, but the design decisions leak into your application: prefer TLS 1.3, rely on ECDHE for forward secrecy, keep your certificate chain complete and your trust store current, use ALPN to negotiate HTTP/2, and treat 0-RTT early data as untrusted and replayable. Enable it only for safe, idempotent operations, and let everything else pay the one extra round trip.
← All articles