πŸ“š Learning Hub
Β· 7 min read

How HTTPS and TLS Actually Work β€” The Handshake Explained


Every time you visit a site with a padlock icon, a small cryptographic ceremony happens before a single byte of your page loads. That ceremony is the TLS handshake, and it’s the reason nobody between you and the server β€” your ISP, the coffee shop Wi-Fi, a state-level adversary β€” can read or tamper with the data in transit.

If you’ve read our intro to HTTPS, you know the β€œwhat.” This post is the β€œhow” β€” the actual mechanics of TLS 1.3, the protocol that secures the modern web.

Why HTTPS Matters

HTTP sends everything in plaintext. Passwords, cookies, API tokens, form data β€” all readable by anyone who can observe the network. HTTPS wraps HTTP inside a TLS tunnel, giving you three guarantees:

  1. Confidentiality β€” data is encrypted; only the client and server can read it.
  2. Integrity β€” data can’t be modified in transit without detection.
  3. Authentication β€” the server proves its identity via a certificate, so you know you’re talking to the real example.com and not an impersonator.

Without HTTPS, things like JWTs and session cookies are just plaintext strings flying across the wire. TLS is the foundation everything else sits on.

Two Kinds of Encryption (Quick Version)

TLS uses both types of encryption, each for a different job:

Asymmetric (public-key) encryption uses a key pair β€” a public key anyone can have and a private key only the owner holds. It’s mathematically expensive, but it solves a critical problem: two strangers can establish a shared secret without ever having met. TLS uses this during the handshake.

Symmetric encryption uses a single shared key for both encrypting and decrypting. It’s fast β€” orders of magnitude faster than asymmetric β€” so TLS switches to it for the actual data transfer once both sides agree on a key.

The handshake’s entire purpose is to use asymmetric crypto to safely agree on a symmetric key, then get out of the way.

The TLS 1.3 Handshake, Step by Step

Here’s what happens in the ~50–100 milliseconds before your browser renders anything. TLS 1.3 completes this in a single round trip (1-RTT):

Client (Browser)                          Server
      |                                      |
      |  1. ClientHello                      |
      |  - supported cipher suites           |
      |  - key share (DH public value)       |
      |  - supported TLS versions            |
      |  ─────────────────────────────────►  |
      |                                      |
      |  2. ServerHello                      |
      |  - chosen cipher suite               |
      |  - server key share (DH public val)  |
      |  ◄─────────────────────────────────  |
      |                                      |
      |  3. Encrypted Extensions             |
      |  + Certificate                       |
      |  + CertificateVerify                 |
      |  + Finished                          |
      |  ◄─────────────────────────────────  |
      |                                      |
      |  4. Finished                         |
      |  ─────────────────────────────────►  |
      |                                      |
      |  ═══ Encrypted application data ═══  |
      |  ◄────────────────────────────────►  |

Let’s walk through each step.

1. ClientHello

The browser opens a TCP connection (after DNS resolution) and immediately sends a ClientHello message. This contains:

  • Supported cipher suites β€” the combinations of algorithms the client can use (e.g., TLS_AES_256_GCM_SHA384).
  • Key share β€” a Diffie-Hellman public value. This is the big TLS 1.3 optimization: the client guesses which key exchange group the server will pick and sends its half of the key exchange upfront, before the server even responds.
  • Supported versions β€” signals that the client supports TLS 1.3.
  • SNI (Server Name Indication) β€” the hostname the client wants, so the server knows which certificate to present (important for shared hosting).

2. ServerHello

The server picks a cipher suite and a key exchange group from the client’s list, then responds with:

  • Chosen cipher suite β€” e.g., TLS_CHACHA20_POLY1305_SHA256.
  • Server key share β€” the server’s half of the Diffie-Hellman exchange.

At this point, both sides independently compute the same shared secret using their private DH values and the other side’s public DH value. No secret ever crosses the wire. This is the magic of Diffie-Hellman key exchange.

3. Server Sends Certificate + Finished

Now encrypted with keys derived from the shared secret, the server sends:

  • Certificate β€” the server’s X.509 certificate, containing its public key and signed by a Certificate Authority.
  • CertificateVerify β€” a signature over the handshake transcript using the server’s private key. This proves the server actually owns the certificate (not just copied it).
  • Finished β€” a MAC over the entire handshake so far, confirming nothing was tampered with.

4. Client Finished

The client verifies the certificate chain, checks the signature, and sends its own Finished message. The handshake is done. Application data flows immediately.

The entire exchange: 1 round trip. The client sends, the server responds, and encrypted data can start flowing.

Certificates and Certificate Authorities

A certificate is the server’s proof of identity. It contains the domain name, the server’s public key, an expiration date, and a digital signature from a Certificate Authority (CA).

How trust works: Your browser and OS ship with a pre-installed list of ~100–150 trusted root CAs. When a server presents a certificate signed by one of these CAs (or by an intermediate CA that chains up to a root), the browser trusts it. If the chain is broken or the CA is unknown, you get the β€œYour connection is not private” error.

What the CA actually verified depends on the certificate type:

  • Domain Validation (DV) β€” the CA checked that you control the domain (most common, what Let’s Encrypt issues).
  • Organization Validation (OV) β€” the CA also verified the organization’s legal identity.
  • Extended Validation (EV) β€” the most thorough check, though browsers have largely stopped giving EV certs special UI treatment.

Why TLS 1.3 Is Faster Than 1.2

TLS 1.2 required 2 round trips before encrypted data could flow:

  1. ClientHello β†’ ServerHello (negotiate parameters)
  2. Key exchange β†’ Finished (establish keys)

TLS 1.3 collapses this to 1 round trip by having the client send its key share in the very first message. The server can respond with everything β€” its key share, certificate, and Finished β€” in a single flight.

There’s also 0-RTT resumption: if a client has connected to a server before, it can send encrypted application data in the very first message using a pre-shared key from the previous session. The tradeoff is that 0-RTT data is replayable, so it’s only safe for idempotent requests (like GETs).

The practical result: TLS 1.3 adds almost no perceptible latency. The β€œHTTPS is slow” argument died years ago.

Let’s Encrypt and the Free Certificate Revolution

Before 2015, getting a TLS certificate meant paying a CA, dealing with manual verification, and remembering to renew. Let’s Encrypt changed everything by offering:

  • Free DV certificates for any domain.
  • Automated issuance and renewal via the ACME protocol (tools like certbot handle it).
  • 90-day certificate lifetimes β€” short enough to limit damage from key compromise, with automated renewal making it painless.

There’s no good reason to run a public site without HTTPS anymore. Let’s Encrypt removed the last barrier.

Certificate Pinning

Certificate pinning is a technique where an application hardcodes (or β€œpins”) the expected certificate or public key for a server. Instead of trusting any certificate signed by any CA, the app only trusts a specific one.

This defends against a compromised or rogue CA issuing a fraudulent certificate for your domain. Mobile apps used this heavily, but it’s fallen out of favor for websites because:

  • It’s easy to brick your app if you rotate certificates without updating the pins.
  • HTTP Public Key Pinning (HPKP) was deprecated by browsers due to the risk of site operators accidentally locking users out.
  • Certificate Transparency logs now provide a public, auditable record of every certificate issued, which catches rogue certs without the brittleness of pinning.

Common TLS Errors Developers Hit

Mixed content warnings β€” Your page loads over HTTPS, but some resources (images, scripts, stylesheets) are loaded over plain HTTP. Browsers block or warn about this. Fix: use relative URLs or https:// everywhere. If you’re dealing with certificate issues in development, check our SSL certificate troubleshooting guide.

Expired certificates β€” Certificates have a validity period. If you forget to renew, browsers will refuse to connect. Let’s Encrypt + certbot with a cron job or systemd timer solves this. Always monitor your cert expiry dates.

Self-signed certificate errors β€” In development, you might generate your own certificate. Browsers won’t trust it because no CA signed it. For local dev, tools like mkcert create locally-trusted certs. Never use self-signed certs in production.

Certificate name mismatch β€” The certificate is for example.com but you’re hitting api.example.com. Make sure your cert covers all the subdomains you need (wildcard certs like *.example.com help here).

TLS version mismatch β€” Older clients that only support TLS 1.0 or 1.1 can’t connect to servers that (correctly) only allow TLS 1.2+. This is a feature, not a bug β€” those old versions have known vulnerabilities.

The Takeaway

TLS is one of those things that works so well most developers never think about it. But understanding the handshake β€” the Diffie-Hellman exchange, the certificate chain, the switch from asymmetric to symmetric β€” makes you better at debugging connection failures, configuring servers, and reasoning about what’s actually secure versus what just feels secure.

The protocol is elegant: use expensive asymmetric crypto once to agree on a key, then switch to fast symmetric crypto for everything else. TLS 1.3 trimmed the fat, cutting the handshake to a single round trip and removing legacy algorithms that were more liability than feature.

Next time you see that padlock, you’ll know what happened in the 80 milliseconds before your page loaded.