HTTPS and the TLS Handshake Explained
HTTPS = HTTP + TLS. The HTTP part is unchanged. TLS adds two things: encryption (so observers can’t read your traffic) and authentication (so you can verify you’re talking to the real example.com, not an impostor). The whole thing happens in a handshake before any HTTP data flows.
What TLS guarantees
- Confidentiality — third parties can’t read what you send
- Integrity — third parties can’t modify it without detection
- Authentication — you’re talking to the real server (verified by certificate)
Note: it does NOT hide WHO you’re talking to. The destination IP and SNI hostname leak. (Encrypted ClientHello / ECH fixes this — newer.)
The TLS 1.3 handshake (1 round trip)
Client Server
| |
| ClientHello |
| - supported versions, ciphers |
| - key share (DH public key) |
|─────────────────────────────────────────→|
| |
| ServerHello + Cert + Finished |
| - chosen cipher |
| - server's DH public key |
| - server's certificate |
| - signature proving cert ownership |
|←─────────────────────────────────────────|
| |
| Finished + first HTTP request (encrypted)|
|─────────────────────────────────────────→|
| |
| HTTP response (encrypted) |
|←─────────────────────────────────────────|
1 RTT total. Compare to TLS 1.2 which needed 2 RTTs.
The 3 things happening at once
Inside that handshake, three independent jobs happen:
1. Cipher negotiation
Client lists ciphers it supports. Server picks the strongest both agree on. Modern: AES-GCM or ChaCha20-Poly1305 with SHA-256 or SHA-384.
2. Key exchange (Diffie-Hellman)
Both sides generate ephemeral key pairs. They exchange public keys and combine them with their private keys to compute the SAME shared secret — without ever transmitting the secret. This is the magic of Diffie-Hellman.
Because keys are ephemeral (regenerated each session), you get forward secrecy: even if the server’s long-term key is later compromised, past traffic stays private.
3. Server authentication
Server sends its X.509 certificate. The cert binds the server’s public key to its hostname (sudoflare.com), signed by a Certificate Authority (CA) that the client trusts. The server proves it owns the cert by signing the handshake with its private key.
The certificate chain
Your browser trusts (built-in trust store):
Root CA (e.g. Let's Encrypt R3 root)
↓ signs
Intermediate CA
↓ signs
Server certificate (sudoflare.com)
Your browser walks up this chain to a root it already trusts. If any link is missing or expired, you get a certificate error.
Common cert errors and what they mean
- NET::ERR_CERT_DATE_INVALID — cert expired. Renew it.
- NET::ERR_CERT_AUTHORITY_INVALID — chain doesn’t lead to a trusted root. Self-signed or wrong issuer.
- NET::ERR_CERT_COMMON_NAME_INVALID — cert is for a different hostname than what you visited.
- NET::ERR_SSL_PROTOCOL_ERROR — server doesn’t support TLS or supports only ancient versions.
Get a certificate (free)
Use Let’s Encrypt via certbot:
# Install
sudo apt install certbot python3-certbot-nginx
# Get cert + auto-configure nginx
sudo certbot --nginx -d sudoflare.com -d www.sudoflare.com
# Auto-renews via systemd timer
Inspect a cert
# From the command line
openssl s_client -connect sudoflare.com:443 -servername sudoflare.com </dev/null 2>/dev/null
| openssl x509 -text
# Quick check of expiration
echo | openssl s_client -connect sudoflare.com:443 2>/dev/null | openssl x509 -noout -dates
What to learn next
HTTP/1.1 vs HTTP/2 vs HTTP/3 — what each version changed and why HTTP/3 abandoned TCP entirely. Up next.