Behind the padlock in your address bar sits a TLS handshake. Between ClientHello and the first encrypted byte, two computers negotiate certificates, cipher choices, and a shared secret nobody else can derive. How does that work — especially the part where two strangers create a shared key in front of eavesdroppers? This guide walks through TLS 1.2 and 1.3 handshakes, certificate chains, SNI, and forward secrecy.
Three problems TLS solves
- Confidentiality — no one can read the traffic (encryption)
- Integrity — no one can tamper with the traffic (MAC / AEAD)
- Authenticity — the server is who they claim (certificates)
Plain HTTP has none of these. On café Wi-Fi, anyone can read or modify your traffic.
TLS 1.2 handshake — two round trips
Client Server
│ │
│──── ClientHello ──────────────────→│ (1st RTT)
│ · TLS version (1.2) │
│ · proposed cipher suites │
│ · client random (28 bytes) │
│ · SNI (server name) │
│ │
│←─── ServerHello ───────────────────│
│ · chosen cipher │
│ · server random │
│←─── Certificate ───────────────────│
│ · server certificate chain │
│←─── ServerKeyExchange (DHE etc) ───│
│←─── ServerHelloDone ───────────────│
│ │
│── 1) verify certificate │
│── 2) generate pre-master secret │
│── 3) encrypt with server pubkey │
│ │
│──── ClientKeyExchange ────────────→│ (2nd RTT)
│──── ChangeCipherSpec ─────────────→│
│──── Finished (encrypted) ─────────→│
│ │
│←─── ChangeCipherSpec ──────────────│
│←─── Finished (encrypted) ──────────│
│ │
│←──── Application Data (encrypted) ─→│ (real traffic begins)Two RTTs. At 50ms Tokyo↔Seoul that's 100ms; at 180ms US↔Korea that's 360ms before any HTTP payload moves. Painful on mobile or satellite links.
TLS 1.3 handshake — one round trip
Client Server
│ │
│──── ClientHello ──────────────────→│ (1st RTT)
│ · TLS version (1.3) │
│ · key share (DHE/ECDHE) up-front│
│ · supported cipher suites │
│ · SNI │
│ │
│←─── ServerHello ───────────────────│
│ · chosen cipher │
│ · server key share │
│ · Certificate (encrypted) │
│ · CertificateVerify (encrypted)│
│ · Finished (encrypted) │
│ │
│── verify certificate │
│ │
│──── Finished (encrypted) ─────────→│
│←──── Application Data (encrypted) ─→│ (immediate)Key differences:
- 1 RTT — the client puts its key share in the first ClientHello
- 0-RTT resumption — for repeat connections, reuse a previous session ticket → zero handshake RTT (GET only, due to replay risk)
- Simpler ciphers — TLS 1.2's 30+ cipher choices collapse to ~5 (AEAD families)
- Forward secrecy mandatory — RSA key exchange removed; only ephemeral DH
Cipher suites — what's actually being chosen
TLS 1.2 format:
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
│ │ │ │ │ │
│ │ │ │ │ └─ MAC algorithm
│ │ │ │ └────── AEAD mode
│ │ │ └────────────── symmetric cipher
│ │ └─────────────────── authentication algorithm (RSA signature)
│ └─────────────────────── key exchange (ECDHE)
└───────────────────────────── protocol
TLS 1.3 format (simplified):
TLS_AES_256_GCM_SHA384
│ │ │
│ │ └─ hash
│ └───────── AEAD
└─────────────── protocolTLS 1.3's cipher suite is simpler because key exchange and authentication are negotiated separately (key_share / signature_algorithms extensions). Far fewer combinations to misconfigure.
Certificates — who vouches for whom
The server presents an X.509 certificate:
Subject: CN=example.com (or SAN: example.com, *.example.com)
Issuer: Let's Encrypt R3
Valid: 2026-05-01 ~ 2026-08-01 (90 days)
Public Key: -----BEGIN PUBLIC KEY----- ... (RSA 2048 or ECDSA P-256)
Signature: signed by Issuer's private key
Extensions: SAN, OCSP, CT log, etc.The client verifies:
- Hostname (CN or SAN) matches the requested domain
- Within validity period
- Issuer's signature is valid (requires Issuer's public key)
- Issuer is a trusted CA
Certificate chain — back to a trusted root
example.com cert ← sent by server
↑ signed by
Let's Encrypt R3 (intermediate CA) ← server also sends this
↑ signed by
ISRG Root X1 (root CA) ← preinstalled in OS / browserOS and browsers ship trust stores with ~200 root CA public keys. Mozilla / Apple / Microsoft update these annually.
SNI — many sites per IP
Without SNI:
Client → 203.0.113.5:443 → "Hello"
Server: which site's cert should I send?
With SNI:
Client → 203.0.113.5:443 → "Hello, host=example.com"
Server: pick example.com's cert and sendThe SNI extension in ClientHello. Required for Cloudflare / AWS / any shared host. But it's plaintext — your ISP can see which site you're visiting.
ESNI / ECH (Encrypted Client Hello) — TLS 1.3 encrypts the SNI too. Cloudflare has it on by default. Slowly rolling out.
Forward secrecy — protecting yesterday's traffic
Old RSA key exchange — if the server's private key ever leaks, all past recorded traffic can be decrypted. A massive risk for long-running services.
Forward secrecy with ephemeral keys — each handshake creates a fresh, throwaway key. Lose the long-term private key, and yesterday's traffic stays secret.
DHE (Diffie-Hellman Ephemeral)
ECDHE (Elliptic Curve DHE) — current standard, faster
How it works:
1. Client and server each generate ephemeral key pairs
2. Exchange the public halves
3. Both derive the same shared secret (DH math — eavesdroppers can't)
4. Throw away the ephemeral private keys after handshakeTLS 1.3 mandates ECDHE — RSA key exchange is gone.
Mutual TLS (mTLS) — the server checks you too
Regular TLS:
- client verifies server cert
mTLS:
- server requests a client cert
- both sides hold a cert + private key
- mutual authenticationWhere it's used:
- Service-to-service auth (Kubernetes service meshes)
- API gateway client auth (instead of passwords)
- Banking / financial APIs
HSTS — preventing HTTP downgrade
Response header:
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
→ Browser enforces HTTPS for this domain for one year
→ http://example.com auto-rewrites to https:// in the browser
→ Cert errors can't be clicked throughPreload — your domain is hardcoded into the Chrome / Firefox HSTS list. The strongest form. Hard to leave once submitted.
Common pitfalls
1. Expired certificates
Let's Encrypt issues 90-day certs. Automated renewal (certbot, Caddy) is essential — a missed cron job equals an outage.
2. Mixed content
<!-- inside an HTTPS page -->
<img src="http://insecure.com/logo.png">
→ Browser blocks or warns (mixed content)3. Weak cipher suites
If the server still allows TLS 1.0 / 1.1 / 3DES / RC4, it's vulnerable. Audit with nmap --script ssl-enum-ciphers -p 443 example.com or SSL Labs.
4. Missing SNI
curl, old Java, Python 2 — requests without SNI hit the default vhost. The cert sent doesn't match → handshake fails.
5. Missing intermediate certificates
If the server omits the intermediate CA from the chain, clients without auto-fetch (common outside browsers) fail validation.
References
- RFC 8446 (TLS 1.3) — datatracker
- The Illustrated TLS Connection — tls13.xargs.org
- SSL Labs Server Test — ssllabs.com
- Mozilla SSL Configuration Generator — Mozilla
Summary
- TLS gives confidentiality, integrity, authenticity. Plain HTTP gives none.
- TLS 1.2 = 2 RTT handshake. TLS 1.3 = 1 RTT + optional 0-RTT resumption.
- Cipher suite = key exchange · authentication · AEAD · hash combination. TLS 1.3 simplified the menu.
- Certificate chain — server cert → intermediate CA → root CA (in the OS / browser trust store).
- SNI tells the server which vhost. Plaintext; ECH is the encrypted successor.
- Forward secrecy via ephemeral keys → past traffic stays safe even if the long-term private key leaks. TLS 1.3 mandates it.
- mTLS = mutual auth. Used in service meshes and API gateways.
- HSTS forces HTTPS. Preload list is the strongest version.
- Try it: HMAC Generator to play with the MAC algorithms TLS uses internally; cURL Builder + curl
-vfor wire traces.