You are probably never going to implement TLS. You are going to configure it, terminate it at a load balancer, argue about where it should end, and get paged when a certificate expires on a Saturday.
That is a different body of knowledge than the one most TLS explanations offer. You do not need the byte layout of a record header. You do need to know why the wrong certificate is being served, what ALPN has to do with your HTTP/2 problem, and whether "we use HTTPS" actually means what your security reviewer thinks it means.
This is that version.
The names, quickly
Most confusion here is vocabulary, not cryptography.
| Term | Reality |
|---|---|
SSL | Obsolete protocol. The word survives in "SSL certificate" and "SSL termination," which both mean TLS |
TLS | The protocol actually securing your traffic |
HTTPS | Plain HTTP carried inside a TLS connection. Same methods, headers, status codes |
TLS 1.3 | The modern default |
TLS 1.2 | Still around purely for compatibility with older clients and appliances |
TLS 1.0 / 1.1 | Disable them |
The stack is worth stating plainly, because a surprising number of arguments dissolve once it is: HTTP is the language, TLS is the secured pipe, HTTPS is the two together. There is no separate "secure HTTP" protocol.
What TLS actually guarantees
Three properties, and it is worth knowing which one you are relying on in any given design discussion:
- Confidentiality: nobody in the path can read the application data
- Integrity: nobody can modify it without the connection failing
- Authentication: the client can verify which server it reached
Everything in the handshake exists to establish one of those three. Note what is not on the list: anonymity, and any guarantee about what happens to the bytes after TLS terminates. Both of those come up later.
The handshake, in one round trip
A fresh TLS 1.3 connection completes in a single round trip. That is not trivia. Every extra round trip is user-visible latency, and it is the main reason 1.3 displaced 1.2.
ClientHello: the capability advertisement
The first message does far more work than the name suggests. It carries the TLS versions the client speaks, the cipher suites it supports, the key agreement groups it knows (x25519, P-256), an ephemeral key_share so agreement can start immediately, the hostname via SNI, the preferred next protocol via ALPN, and its supported signature algorithms.
Sending key_share up front is the design move that buys the single round trip. Older handshakes needed more back-and-forth before either side had usable secret material.
This message is plaintext, and it has to be. No shared keys exist yet. So an observer sees the handshake happening, both IPs, the port, timing, packet sizes, and usually the hostname you asked for, unless ECH is in play.
ServerHello: the turning point
The server picks the parameters: this version, this cipher suite, here is my key share. The moment the client has that key share, both sides can independently derive shared secrets, and the connection stops negotiating and starts building real cryptographic state.
Encryption begins mid-handshake
This is the detail most mental models get wrong. The common picture is public handshake → handshake ends → encryption starts. That is not TLS 1.3.
There are two distinct transitions, and keeping them separate makes the whole protocol clearer:
- plaintext negotiation → encrypted handshake traffic (right after
ServerHello) - encrypted handshake → encrypted application data (after trust checks pass)
One practical consequence: in TLS 1.3 the certificate chain itself is encrypted. A passive observer cannot read it off the wire the way they could in 1.2.
The server messages, and why each one exists
Easy to treat as one block. They are not.
EncryptedExtensions: the rest of the negotiated connection details, now that handshake encryption is available.
Certificate: the chain. It answers one question: which public key belongs to this hostname, according to a trust chain you might accept. It does not mean the connection is trustworthy.
CertificateVerify: the proof-of-possession step, and the one people forget. Without it, anyone could replay a public certificate chain they downloaded. This message signs the handshake transcript, proving the server holds the matching private key in this specific handshake.
Finished: the transcript integrity check. It proves the sender derived the expected secrets and saw the same conversation. If someone tampered with the earlier plaintext negotiation, verification fails here.
Then the client does its own verification (hostname match, chain to a trusted root, validity window, signature, transcript) and only then sends its Finished. Before that point the client can compute keys, but it must not treat the server as authenticated.
After that: ordinary HTTP.
Two ideas worth internalizing
Keys are derived, not transmitted. Neither side ever sends the session secret. Each combines its own private key material with the other's public material and arrives at the same value independently. That is why the handshake survives a hostile network.
Certificates prove identity; key agreement provides secrecy. These get blurred constantly, and the blur causes bad architecture decisions. A certificate binds a key to a name. The secrecy of your session comes from the ephemeral key agreement and the traffic keys derived from it. They are separate jobs, composed carefully.
The ephemeral part gives you forward secrecy: if the server's long-term private key leaks next year, that alone does not decrypt traffic captured today. TLS 1.3 makes this mandatory rather than optional.
Where the trust boundary actually ends
Here is the part that matters most for backend work, and the part diagrams usually lie about.
Almost nothing runs one continuous TLS connection from browser to application process. A realistic path:
Browser ──TLS──▶ CDN ──TLS?──▶ Load balancer ──TLS?──▶ Ingress ──TLS?──▶ Your service
▲ ▲ ▲
terminates re-encrypts? plaintext?
Each of those hops is a decision, and each TLS? is a question somebody has to have actually answered. "We use HTTPS" describes the first hop only. The security property you care about is where TLS terminates and what protects every hop after it.
This is where real incidents live: TLS terminated at the CDN, and everything behind it assumed safe because it is "internal." That assumption is a choice, and it should be a documented one rather than an accident of default config.
The knobs you will actually touch
SNI tells the server which hostname the client wants, so one IP can serve many certificates. When SNI is misconfigured you get the wrong certificate served while every other part of the stack looks healthy. That symptom is distinctive; remember it.
ALPN is how both sides agree on what runs on top of TLS, usually h2 versus http/1.1. This is negotiated during the handshake, which surprises people debugging HTTP/2. If a proxy in the path does not offer h2, you silently get HTTP/1.1 and a pile of confusing performance numbers.
Session resumption and 0-RTT: a returning client can reuse trusted state and skip the full handshake. TLS 1.3 also allows 0-RTT early data, sending application data before the handshake completes. The tradeoff is real: early data can be replayed. That makes it acceptable for idempotent reads and dangerous for anything that mutates state. It is a performance feature with application-level consequences, not a free win.
mTLS: the client presents a certificate too. Irrelevant for public web browsing, central to service-to-service auth, where you want a cryptographic identity for the caller rather than a bearer token that can be copied out of a log.
What still leaks
TLS is not invisibility, and conflating the two derails threat-model conversations.
| Usually visible | Protected |
|---|---|
| Source and destination IPs, ports | HTTP methods, paths, headers |
| Timing, duration, traffic shape | Cookies, tokens, API payloads |
| That TLS is being used | Request and response bodies |
| The plaintext early handshake | The later handshake and certificate chain |
Hostname via SNI, absent ECH |
TLS protects the contents of the conversation. It does not hide the existence or shape of it.
Where it breaks in production
The failures are almost never cryptographic. They are trust, configuration, and time.
- Expired certificate, or renewal automation that silently stopped
- Hostname mismatch: certificate does not cover the name actually requested
- Incomplete chain: works in your browser, which caches intermediates, and fails from a bare container that does not
- Wrong certificate served, from
SNImisconfiguration - No overlap in supported versions or cipher suites, typically an old client against a hardened server
ALPNdisagreement quietly downgrading you to HTTP/1.1- Clock skew making a valid certificate look expired or not-yet-valid
- TLS terminated at the edge with downstream hops assumed secure and never audited
The incomplete-chain one deserves emphasis because it is the most common "works on my machine" TLS bug there is.
Three commands cover most of the diagnosis:
# What was actually negotiated, and what chain was served
openssl s_client -connect example.com:443 -servername example.com
# Just the validity window
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null \
| openssl x509 -noout -dates -subject -issuer
# Confirm ALPN and TLS version from the client's point of view
curl -vI https://example.comPass -showcerts to s_client when you suspect a missing intermediate, and -tls1_2 or -tls1_3 to test whether a specific version negotiates at all.
The version to keep in your head
HTTPSisHTTPoverTLS.SSLis a dead word for a live problemClientHelloadvertises capabilities and starts key agreement immediately, which is where the one-round-trip handshake comes from- Encryption starts mid-handshake, not after it
- Certificates prove identity; ephemeral key agreement provides secrecy and forward secrecy
CertificateVerifyis what stops anyone from replaying a public chain- The security property that matters operationally is where TLS terminates, not whether it is enabled
- Metadata still leaks. Encrypted is not opaque
- When it breaks, look at expiry, chain completeness, hostname,
SNI,ALPN, and the clock, in that order
The math is not going to fail you. The configuration will.