You've shipped a feature behind HTTPS and called it secure. Then a pentest comes back flagging TLS 1.0 support, weak cipher suites, or a misconfigured certificate chain — and you realise you've been treating HTTPS as a checkbox, not a protocol you understand. This guide fixes that.
The Problem with "Just Use HTTPS"
HTTPS isn't a single thing. It's HTTP layered over TLS (Transport Layer Security), and TLS itself is a negotiation — the server and client agree on algorithms, exchange keys, verify identities, and only then start sending data. Every step in that process has options, and some of those options are catastrophically weak.
A server that supports TLS 1.0, RC4 ciphers, or SHA-1 signed certificates is "using HTTPS" in the same way a screen door is "using a lock."
The TLS Handshake, Step by Step
When your browser connects to https://example.com, here's what actually happens before a single byte of HTTP is transmitted:
1. ClientHello
The client sends:
- TLS version(s) it supports
- A random 32-byte nonce (
client_random) - A list of supported cipher suites (e.g.,
TLS_AES_256_GCM_SHA384) - Supported extensions (SNI, ALPN, etc.)
SNI (Server Name Indication) is how a single IP address can serve multiple HTTPS domains. The hostname is sent in plaintext here — which is why SNI leaks your destination even over HTTPS without ECH (Encrypted Client Hello).
2. ServerHello
The server responds with:
- The TLS version selected
- Its own random nonce (
server_random) - The chosen cipher suite
- Its certificate (or certificate chain)
3. Certificate Verification
The client verifies:
- The cert is signed by a trusted CA (following the chain up to a root)
- The hostname matches the cert's CN or SAN fields
- The cert hasn't expired
- The cert hasn't been revoked (via OCSP or CRL)
Gotcha: Intermediate certificates must be bundled with the server cert. Desktop browsers silently fetch missing intermediates via AIA (Authority Information Access). Mobile clients and most HTTP clients in Node.js, Java, and Python do not. A broken chain that "works in Chrome" will cause CERTIFICATE_VERIFY_FAILED in production API calls and cause you to spend two hours questioning your sanity.
4. Key Exchange
This is where the session key gets established. In TLS 1.2, this is typically RSA or ECDHE. In TLS 1.3, it's always ECDHE (Elliptic Curve Diffie-Hellman Ephemeral).
With ECDHE:
- Both sides contribute to the key material
- The final session key is never transmitted over the wire
- Each session uses a fresh key pair — this is forward secrecy
Forward secrecy means that even if the server's private key is stolen later, past sessions can't be decrypted. With RSA key exchange (old TLS 1.2), all past traffic is retroactively decryptable if the private key is compromised.
5. Finished
Both sides compute a MAC over the full handshake transcript and verify they match. Any tampering during the handshake would be detected here.
TLS 1.3 Handshake
Press Play to walk through the TLS 1.3 handshake
Cipher Suites: Where Configuration Goes Wrong
A cipher suite defines the algorithms used for each part of the connection. Example:
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
Breaking this down:
ECDHE_RSA— key exchange (ECDHE) + certificate type (RSA)AES_256_GCM— symmetric encryption algorithm and modeSHA384— MAC/HMAC for integrity
Avoid:
RC4— broken, known keystream biases3DES/DES— SWEET32 vulnerability, too slowCBCmode suites in TLS 1.2 — BEAST, Lucky13 attacksNULLencryption suites — yes, these exist and some servers enable themEXPORTsuites — backdoored by design (FREAK attack)
Use:
TLS_AES_128_GCM_SHA256orTLS_AES_256_GCM_SHA384(TLS 1.3 only)TLS_CHACHA20_POLY1305_SHA256— better performance on mobile/ARM
In TLS 1.3, cipher suite selection is drastically simplified. There are only five options, all strong. This is the right answer to the question "which cipher suites should I configure?" — upgrade to TLS 1.3 and let the spec make the decision for you.
Certificates: The Chain of Trust
Your certificate proves to clients that you are who you say you are. The trust model works like this:
- Root CA — Pre-installed in OS/browser trust stores (DigiCert, Let's Encrypt ISRG Root, etc.)
- Intermediate CA — Signed by the root, used for day-to-day issuance
- Leaf certificate — Your domain cert, signed by the intermediate
Your server must send the leaf cert plus all intermediate certs. Not the root (that comes from the trust store).
Certificate Fields That Actually Matter
- SAN (Subject Alternative Name) — The actual list of domains the cert covers. CN is legacy and ignored by modern clients.
- Not Before / Not After — Expiry. Automate rotation. Let's Encrypt certs expire in 90 days by design to force automation.
- Key Usage / Extended Key Usage — Cert must have
serverAuthfor HTTPS. Using a code-signing cert for TLS is a misconfiguration that some servers will silently accept and clients will reject.
Symmetric Encryption: The Actual Data Transfer
Once the handshake completes, both sides have the same session key (derived via HKDF from the handshake key material). All HTTP data is encrypted with AES-GCM or ChaCha20-Poly1305.
These are AEAD (Authenticated Encryption with Associated Data) ciphers — they provide confidentiality and integrity in one primitive. Every record includes an authentication tag; tampering with the ciphertext causes decryption to fail.
Record size matters here: TLS splits data into records of up to 16KB. An attacker watching traffic can still infer things from record sizes and timing even when content is encrypted. This is why HTTPS doesn't fully prevent traffic analysis.
Practical Code: Verifying TLS Configuration
Common Misconfigurations
HSTS preloading is one-way
Once you submit your domain to the HSTS preload list, removing it takes months to propagate. Don't preload until you're certain you'll never need plain HTTP on that domain again.
Mixed content breaks silently in different ways
Active mixed content (scripts, iframes) is blocked. Passive mixed content (images) may load with a warning. Either kills the padlock and can allow partial page hijacking.
Certificate pinning is a maintenance trap
Pinning the server's cert (not the CA) means a cert rotation breaks your mobile app until users update. Pin the CA or intermediate, set a backup pin, and have a rotation plan before you ship.
TLS termination at the load balancer means internal traffic is unencrypted
Common in Kubernetes setups. If your internal network is compromised, all traffic between services is plaintext. Use mutual TLS (mTLS) between services if this is a concern.
Run testssl.sh or SSL Labs against your domains right now. Look for: TLS 1.0/1.1 support, CBC cipher suites, missing HSTS headers, and incomplete certificate chains. Fix in that order. Then enforce TLS 1.2 minimum (TLS 1.3 preferred) in your reverse proxy or load balancer config, disable all cipher suites that don't provide forward secrecy, and automate certificate rotation before you touch anything else.
The padlock doesn't mean you're secure. It means you're encrypted — and only if the configuration behind it is sound.
Frequently Asked Questions
Related posts
Secure Password Reset Tokens — Expiry, Storage, and What Most Implementations Get Wrong
A practical guide to building secure password reset flows: token generation, expiry windows, one-time use enforcement, and the edge cases that cause real account takeovers.
Mar 30, 2026 · 7 min readIncident Response for Developers: What to Do When You Get Hacked
A practical incident response guide for developers covering detection, containment, eradication, recovery, and communication when a security breach happens.
Mar 29, 2026 · 9 min readPhishing Prevention: A Developer's Guide to SPF, DKIM, and DMARC
Understand how email spoofing enables phishing attacks and how to implement SPF, DKIM, and DMARC to protect your domain from being impersonated.
Mar 29, 2026 · 9 min read