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:

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:

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:

  1. Building a path from the leaf certificate up to a trusted root.
  2. Verifying each signature in the chain with the issuer's public key.
  3. 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:

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.

tlshttpscryptographynetworkingsecurity
← All articles