14 min read · Guide · Network
How it works · Network · Security

How HTTPS secures a connection before the page loads

HTTPS protects most web traffic. This guide describes in order what happens in the milliseconds before a page loads, from the first packet to the first encrypted request.

Parts01 – 06 InteractiveTLS 1.2 / 1.3 PrereqTCP / DNS

The three guarantees HTTPS makes

HTTPS is HTTP over TLS. TLS is the encryption layer that secures modern web requests. TLS 1.3 (RFC 8446, 2018) is the current standard, replacing TLS 1.2 (2008) and the deprecated SSL series. The handshake establishes a shared secret between client and server using public-key cryptography; once established, symmetric encryption protects all subsequent data.

HTTPS provides three separate guarantees, not just encryption. Without all three the connection cannot be considered secure, and each one is defeated by a different kind of attacker.

01

Confidentiality

ISPs, routers and Wi-Fi snoopers cannot read the payload. After the handshake the bytes look like random noise to anyone who doesn't hold the session key.

02

Integrity

The data cannot be modified in transit without the modification being detected. Authenticated encryption attaches a tag; flip a single bit of ciphertext and decryption fails.

03

Authentication

You are talking to the server you meant to reach, not an impostor on the same coffee-shop network or someone holding a forged cert. This relies on PKI, covered in Part 04.


The two kinds of cryptography TLS uses

TLS combines two kinds of cryptography with opposite trade-offs, and uses each only for the job it suits.

Asymmetric · slow

Padlock and key

Anyone can snap the padlock shut (encrypt with a public key), but only the person holding the physical key (the private key) can open it. RSA and elliptic-curve variants work this way. The math is expensive, far too slow to stream video through, so TLS uses it only during the handshake to safely agree on a shared secret.

Symmetric · fast

One shared secret

A single key that both sides hold. AES-256 in GCM mode is typical, and it is hardware-accelerated on modern CPUs, fast enough to encrypt gigabits per second per core. Once the handshake hands over the shared secret, all bulk data is symmetric from there on.

Summary

TLS = asymmetric math (slow) + symmetric math (fast). The slow math exists only to deliver the key for the fast math, and once that key is in place it isn't used again for the session.


The TLS handshake, one message at a time

Before a single byte of application data is sent, the client and server perform a short, fixed exchange called the TLS handshake. Toggle between the modern version (TLS 1.3) and its predecessor (TLS 1.2) below, and compare the round trips. TLS 1.3 finishes in one; 1.2 needs two. That difference is a large part of why 1.3 exists.

CLIENTBrowser198.51.100.24SERVERexample.com93.184.216.34 01ClientHello 02 ServerHello · Certificate · Finished 03 Finished 04 Application data 1-RTT · TLS 1.3
Step 01 of 04

The client announces what it can speak and pre-ships a key share, so the server can derive the shared secret on receipt, which saves a round trip. SNI names the host; ALPN offers application protocols.


How certificates build a chain of trust

The handshake depends on the client verifying the server's certificate. What stops an attacker from handing a browser a fake cert that says "I am yourbank.com"?

The answer is the chain of trust. Every operating system and browser ships with a pre-installed root store holding the keys of a few dozen trusted third parties called Certificate Authorities (DigiCert, Sectigo, GlobalSign, Let's Encrypt and others).

  1. 01

    Domain validation

    You construct a Certificate Signing Request containing your public key and send it to a CA. They verify (usually via a file you place on your web server, or a DNS record you create) that you control the domain.

  2. 02

    CA signature

    Once validation passes, the CA signs your certificate with its private key, usually through an intermediate certificate that chains up to a root. Anyone who has the CA's corresponding public key can verify the signature.

  3. 03

    Client verification

    When a browser connects, your server presents the signed certificate. The browser checks the signature, finds the matching root in its root store, and verifies the chain. If it verifies, the browser trusts the certificate. If not, the user sees a full-page certificate warning.


What perfect forward secrecy protects

The classic RSA key exchange had a serious weakness: if an attacker recorded months of your encrypted traffic and later stole your server's private key, they could decrypt every recorded session.

The fix is called Perfect Forward Secrecy, implemented in modern TLS via Ephemeral Elliptic-Curve Diffie-Hellman (ECDHE). Instead of the server's private key being used to decrypt a shared secret, the client and server dynamically compute a brand new ephemeral key pair for every session. Diffie-Hellman lets two parties exchange public values and independently arrive at the same shared secret, without the secret itself ever crossing the wire.

The server's long-lived private key is used only for authentication. It signs a message during the handshake to prove identity, but it is not involved in deriving the encryption keys. If the server is compromised tomorrow, yesterday's recorded traffic stays secure.


What TLS 1.3 changed: lighter and faster

Finalized in 2018 as RFC 8446, TLS 1.3 was a substantial rewrite. It removed legacy features, hardened the defaults and cut the handshake from two round trips to one.

What was removed

Removing legacy ciphers

TLS 1.2 kept dozens of insecure ciphers (RC4, DES, SHA-1) for backward compatibility. Attackers exploited this with downgrade attacks, tricking the negotiation into picking broken crypto. TLS 1.3 deleted them and keeps five AEAD cipher suites.

What was added

1-RTT by default

With only a few ciphers and groups to pick from, the client can guess what the server will accept and include its DH key share in the very first packet. The server replies with its share, its certificate and Finished in one flight, so the handshake takes half the round trips of TLS 1.2.

Zero round trips on resumption

TLS 1.3 also added 0-RTT resumption. If a user has visited recently, the client and server hold a resumption ticket, and on the next visit the browser can encrypt the GET request and send it alongside the ClientHello. On reconnects, the latency is then close to that of unencrypted HTTP.


SNI and ALPN: two ClientHello extensions

The ClientHello carries more than ciphers and random values. Two small extensions matter here: one selects the certificate, the other selects the protocol that will run on top.

SNI · Server Name Indication

Choosing the certificate

One IP can host hundreds of sites. Before the server can choose which certificate to present, it needs to know which hostname the client is trying to reach, because a cert is pinned to a name. SNI puts that hostname in the ClientHello in the clear (pre-handshake, there is no encryption yet), so routers and load balancers can also dispatch to the right backend without decrypting anything.

ALPN · Application-Layer Protocol Negotiation

Choosing the application protocol

The client advertises which application protocols it supports: h2 for HTTP/2, http/1.1 for the classic, or both in preference order. The server picks one, echoes it back in the ServerHello, and that choice is locked in for the life of the connection. There is no upgrade step and no second round trip to discover HTTP/2 support. ALPN is also what HTTP/3 uses to be pointed at (via Alt-Svc and the h3 token) once QUIC is in play.

SNI has a privacy cost: because it's sent in cleartext, a passive observer knows which site you're visiting even though they can't read what you do there. Encrypted Client Hello (ECH), the successor to ESNI, wraps the entire ClientHello (including SNI) in a second layer of encryption using a public key the front-end publishes in DNS. It is now standardised as RFC 9849, and Cloudflare and Firefox already ship it. SNI, its routing tricks, and ECH get the full treatment in the dedicated SNI guide.


How to harden the chain of trust

PKI on its own has gaps. Certificates get compromised, attackers can strip HTTPS on the first visit before HSTS takes effect, and CAs occasionally misissue or get tricked. Three mechanisms close most of these gaps.

OCSP Stapling

The server proves the cert isn’t revoked

When a cert is compromised, the CA revokes it, but the browser still has to find out. The simple approach is an OCSP query to the CA at connection time, which adds latency and leaks your browsing history to the CA. Stapling reverses this: the server periodically fetches a fresh, signed OCSP response from the CA and attaches (staples) it to its own handshake. The browser verifies the short-lived signature without contacting the CA, which is faster and keeps browsing history private.

HSTS

Forcing HTTPS on every future visit

An Strict-Transport-Security header tells the browser: "for the next year, refuse to talk to me over HTTP, even if the user types http:// or clicks a plaintext link." Once cached, downgrade attacks are impossible for that period. The HSTS preload list, shipped with every browser, closes the first-visit gap for domains that opt in.

CT Logs

Every certificate is publicly logged

Certificate Transparency requires every publicly-trusted cert to be submitted to tamper-evident append-only logs. The CA receives a Signed Certificate Timestamp in return, which is embedded in the cert itself. Browsers refuse certs without SCTs. If someone obtains a rogue cert for your domain, you (or a monitor such as crt.sh) can spot it in the logs within minutes.


Inside a ClientHello, field by field

The handshake from Part 02 is a sequence of TLS records. Each record is at most 214 bytes (16 KiB), wraps a typed payload (handshake, application data, alert, change cipher spec), and carries a 5-byte header. The first record a client sends is a ClientHello, which lists everything the client supports.

# TLS RECORD HEADER (5 bytes)
16 03 01 00 a3              # type=Handshake(22), version=TLS1.0(legacy), length=0x00a3

# HANDSHAKE HEADER (4 bytes)
01 00 00 9f                  # msg_type=ClientHello(1), length=0x00009f

# CLIENT HELLO BODY
03 03                        # legacy_version = TLS1.2 (0x0303) — 1.3 negotiates via extension
54 7c…32 random bytes…       # ClientRandom (32B) — feeds the key schedule

00                           # legacy_session_id (empty in 1.3)

00 06                        # cipher_suites length = 6
13 01  13 02  13 03          # TLS_AES_128_GCM_SHA256, AES_256_GCM, CHACHA20_POLY1305

01 00                        # compression_methods = [null] — never negotiated in TLS 1.3

00 6e                        # extensions length = 110 bytes
  00 00  00 0d  …            # server_name (SNI): "api.example.com"
  00 0a  00 04  00 1d 00 17  # supported_groups: x25519, secp256r1
  00 0d  00 0a  …            # signature_algorithms: ed25519, rsa_pss_rsae_sha256, ecdsa_secp256r1_sha256
  00 33  00 26  …            # key_share: x25519 public key (32 bytes)
  00 2d  00 02  01 01        # psk_key_exchange_modes: psk_dhe_ke
  00 2b  00 03  02 03 04     # supported_versions: TLS 1.3 (0x0304)
  00 10  00 0e  …            # ALPN: h2, http/1.1

The body is a fixed prologue plus a free-form list of extensions. TLS 1.3 freezes the prologue (legacy fields are kept identical for middlebox compatibility) and moves all real negotiation into extensions: the version, the key exchange group, the public key, the SNI hostname and the ALPN protocol list. The server only echoes what it accepts, and ignores any extension it doesn't recognise.

SNI

Telling the server which certificate to send

Many sites share one IP. Without Server Name Indication the server wouldn't know which certificate to present. SNI travels in the clear in the ClientHello, which is why Encrypted Client Hello (ECH, RFC 9849) wraps the inner ClientHello inside an outer one whose SNI names only the frontend provider.

Key Share

Guessing the key group to save a round trip

In TLS 1.2 the client first asked which group the server supported, then sent the public key. TLS 1.3 fuses both: the client guesses (usually X25519) and includes a key share for it. If the server agrees, the handshake completes in 1-RTT. If the server wants a different group, it replies with a HelloRetryRequest and the client sends another ClientHello, which costs one extra round trip.

ALPN

Agreeing on HTTP/2 vs HTTP/1.1 in the handshake

Application-Layer Protocol Negotiation chooses between h2, h3, and http/1.1 inside the same handshake. There is no second round trip after TLS finishes. ALPN is also how WebRTC (webrtc) and gRPC (h2) avoid sniffing.

In your own terminal

openssl s_client -connect example.com:443 -servername example.com -tls1_3 -trace dumps every record in this format, and the -trace flag labels each field. For a live Wireshark filter, use tshark with tls.handshake.


The five cipher suites TLS 1.3 kept

TLS 1.2 had 37 standard cipher suites and over three hundred legacy ones: RC4, 3DES, MD5, RSA key exchange, anonymous DH. Misconfiguration was common. TLS 1.3 cut the menu to five, all AEAD, all forward-secret. Key exchange, signature and certificate type moved to extensions, where each can change without touching the rest.

Suite Cipher Hash (HKDF) Where it shines
TLS_AES_128_GCM_SHA256AES-128-GCMSHA-256Default. Fast on hardware with AES-NI.
TLS_AES_256_GCM_SHA384AES-256-GCMSHA-384Compliance ceilings (NSA Suite B, FIPS).
TLS_CHACHA20_POLY1305_SHA256ChaCha20-Poly1305SHA-256Mobile / no AES-NI. Constant-time, no timing attacks.
TLS_AES_128_CCM_SHA256AES-128-CCMSHA-256IoT / DTLS where GCM hardware isn't available.
TLS_AES_128_CCM_8_SHA256AES-128-CCM-8SHA-256Constrained devices. 8-byte tag instead of 16.
Key exchange

Always (EC)DHE.

Every TLS 1.3 session uses ephemeral Diffie-Hellman for the key. RSA key transport is gone. The supported_groups extension lists curves: x25519 (default; 32-byte keys, fast, formally analysed), secp256r1 / secp384r1 (NIST curves; required by FIPS), x448 (larger curve, higher security level). Ephemeral keys mean a stolen long-term private key cannot decrypt past traffic: perfect forward secrecy is on by default.

Signature

Certificate and signature

The server signs the transcript with the private key matching its certificate. signature_algorithms negotiates the algorithm: ed25519 (modern, fast, deterministic), ecdsa_secp256r1_sha256 (most common ECDSA), rsa_pss_rsae_sha256 (RSA, but properly padded). MD5 and SHA-1 signatures are flatly forbidden: past attacks proved a downgrade existed in TLS 1.2.

Why AEAD matters

Pre-1.3 TLS used MAC-then-encrypt as a separate composition, and a series of attacks (Lucky13, BEAST, POODLE) lived in that gap. AEAD ciphers do encryption and authentication in one operation: every byte of ciphertext is bound to its position and to the associated plaintext header. There is no padding oracle to leak through, no MAC truncation to exploit. The 1.3 cipher list is short on purpose.


Inspecting TLS from the command line

Most TLS production failures are a wrong certificate chain, an expired cert, a name mismatch, a protocol downgrade or SNI not configured. Five tools cover almost every diagnosis from the terminal.

  1. 01

    Inspect the chain. openssl s_client

    openssl s_client -connect example.com:443 -servername example.com -showcerts. Prints the entire certificate chain the server sent, in order. Use -tls1_3 to force the version, -status to also fetch the OCSP staple, and < /dev/null to close immediately after the handshake instead of hanging on stdin.

  2. 02

    Decode a single cert. openssl x509

    openssl x509 -in cert.pem -noout -text dumps the certificate as readable text: Subject, Issuer, validity window, Subject Alternative Names, key algorithm, fingerprint, embedded SCTs. -checkend 86400 is useful for monitoring because it exits non-zero if the cert is within 24 hours of expiry.

  3. 03

    Walk the connection. curl -v

    curl -v https://example.com shows the resolved IP, the cipher suite, the negotiated ALPN protocol, the chain verification result, and the response headers. Add --resolve example.com:443:1.2.3.4 to test a specific origin behind a load balancer, and --cacert to point at a custom root for self-signed environments.

  4. 04

    Verify against a public scan. testssl.sh

    testssl.sh --severity HIGH https://example.com runs a full audit: protocol versions, cipher suites, vulnerability scans (Heartbleed, ROBOT, Logjam), HSTS posture, CT log presence. It is open source and runs locally. Pairs with ssllabs.com/ssltest for a public report card.

  5. 05

    Watch the wire. tshark / wireshark

    tshark -i any -f "host example.com and port 443" -O tls filters the live traffic and parses every TLS record. To decrypt your own connections, set SSLKEYLOGFILE=/tmp/keys.log in the environment of the originating browser or curl; Wireshark reads the keylog and shows you the application data in plaintext.

Common error decoder

SSL_ERROR_NO_CYPHER_OVERLAP: the client's cipher list and the server's don't intersect. Usually a TLS 1.3-only server talking to an ancient client.
unable to get local issuer certificate. The server didn't send the intermediate; install the full chain, not just the leaf.
subjectAltName: no name matching. The cert's SANs don't include the requested host. CN-only certs stopped being trusted in browsers years ago.
certificate expired. The most common alert of all. Set up a x509 -checkend monitor.

Modern TLS in production: Let's Encrypt, mTLS, post-quantum

What production TLS looks like in practice.

Let's Encrypt is the default for most sites. Free, automated, 90-day certificates via the ACME protocol have helped drive HTTPS to roughly 95% of page loads (Google's HTTPS transparency report). Tools such as certbot, acme.sh and Caddy's automatic HTTPS make deployment easy. Paid certificates remain mainly for organisation-validated (OV) and extended-validation (EV) types, which browsers no longer visually distinguish.

mTLS for service-to-service. In mutual TLS both sides present certificates, and it is the dominant pattern in modern service meshes. Istio, Linkerd and AWS App Mesh issue and rotate workload certificates automatically (typically via SPIFFE-style identity). Certificate management used to be a heavy operational cost, and service mesh automation made mTLS cheap to run.

Post-quantum readiness. Quantum computers running Shor's algorithm would break RSA and ECDH at scale. NIST standardised the first post-quantum KEM as ML-KEM (FIPS 203, née Kyber). Cloudflare and Google ship hybrid TLS handshakes (X25519 + ML-KEM) in production; the long tail of embedded and legacy clients means the full transition will take around a decade.

HSTS preload list. Over 100,000 domains are on the browser-bundled HSTS preload list, so browsers refuse HTTP to them even on the first visit. The one-time submission is worth making for any production site.

Certificate Transparency. Every cert from a public CA must appear in a public CT log within seconds of issuance. crt.sh lets you search them; production teams use this to detect rogue certificates issued for their domains.



A closing note

Ten years ago, mostly banks enforced HTTPS. Certificates were expensive, handshakes were slow, and most of the web stayed plaintext. Today Let's Encrypt issues certificates for free in seconds, AES-NI makes bulk encryption cheap, and browsers only speak HTTP/2 and HTTP/3 over TLS. HTTPS is now the default transport for almost everything you load.


Three quick checks

Test what you just read about the TLS handshake.

Pick an answer for each. Every answer, right or wrong, shows a short explanation, and there is no scoring.

Q1. TLS 1.3 stripped one full round trip out of the handshake. How does it do that?
Q2. A browser receives the header Strict-Transport-Security: max-age=31536000. What does that change about future requests to that origin?
Q3. A self-signed certificate triggers the big red warning page in every browser. Why?

Further
reading

Found this useful?

Published Updated