TLS, HTTPS, Certificates
TLS (Transport Layer Security) encrypts a TCP connection. HTTPS = HTTP over TLS. Modern web is TLS 1.2 and 1.3; everything older (SSL 2/3, TLS 1.0/1.1) is deprecated.
What TLS gives you
- Confidentiality — observers see ciphertext.
- Integrity — tampering is detected.
- Authentication — the server proves it’s who the cert says it is. (Mutual TLS authenticates the client too.)
It does not give you:
- Authorization. TLS proves identity; your app decides what they can do.
- Hiding metadata fully. The destination IP, port, packet sizes, SNI hostname (in TLS <1.3 without ECH) are visible.
TLS 1.3 handshake (1-RTT)
Client ──ClientHello (key share, cipher suites, SNI)──▶ Server
Client ◀── ServerHello + cert + EncryptedExtensions + Finished ── Server
Client ──ChangeCipherSpec + Finished──▶ Server
( application data starts; total 1 RTT, 0 RTT for resumption )
TLS 1.2 took 2 RTTs. TLS 1.3 cut it to 1 (and 0 for session resumption). On a 100ms RTT link, that’s saving 100–200ms before HTTP can even start.
Certificate basics
A certificate is a signed claim: “this public key belongs to api.example.com, signed by [CA], valid from X to Y.” Browsers/OSes ship a list of trusted root CAs (~150). A cert is valid if there’s a chain from it back to one of those roots.
[ Root CA cert ] ← trusted by OS/browser, self-signed
│ signs
[ Intermediate CA cert ]
│ signs
[ api.example.com cert ] ← what your server presents
The server presents the leaf + intermediate(s); the client already trusts the root. Forgetting to include the intermediate is the #1 cert misconfiguration — Chrome may still work (it fetches missing intermediates) but curl and Python’s requests fail.
Certificate types
| Type | Validates |
|---|---|
| DV (Domain Validated) | you control the domain (HTTP/DNS challenge). Let’s Encrypt is DV. |
| OV (Organization Validated) | DV + manual check that org exists |
| EV (Extended Validation) | OV + extra paperwork. Used to show green-bar in browsers; browsers stopped highlighting it. |
| Wildcard | *.example.com — covers any single-level subdomain |
| SAN (Subject Alternative Name) | one cert covers multiple specific names |
For 99% of services, a free DV cert from Let’s Encrypt is fine. EV is largely vestigial.
Let’s Encrypt + ACME
Let’s Encrypt issues free DV certs valid for 90 days. The ACME protocol automates issuance and renewal:
- Client (Certbot, acme.sh, Caddy built-in) generates a key and CSR.
- Tells Let’s Encrypt “I want a cert for example.com.”
- Let’s Encrypt challenges: prove you own it.
- HTTP-01: serve a token at
http://example.com/.well-known/acme-challenge/<token>. - DNS-01: create a TXT record
_acme-challenge.example.com. - TLS-ALPN-01: rare, used when you can’t open port 80.
- HTTP-01: serve a token at
- Let’s Encrypt fetches/queries the proof.
- Cert issued. Renewal runs automatically every 60 days.
Caddy and Traefik do this transparently. nginx + certbot is the manual standard. AWS ACM does it for AWS LBs (free, auto-renewed, but only usable on AWS resources).
SNI — multiple sites, one IP
Server Name Indication is a TLS extension where the client sends the hostname during the handshake so the server can pick the right cert. Without SNI, one IP could only host one HTTPS cert.
In TLS 1.2, SNI is plaintext (observers see which site you’re visiting). TLS 1.3 + ECH (Encrypted Client Hello) hides it. ECH is rolling out; Cloudflare supports it server-side, browsers gradually.
mTLS (Mutual TLS)
Server presents a cert; client also presents a cert. Both validate each other.
Use cases:
- Service-to-service inside a mesh (Istio/Linkerd auto-issue and rotate).
- High-security APIs (banking, government).
- Replacing API keys with cryptographic identity.
In Python:
import httpx
client = httpx.Client(cert=("client.crt", "client.key"), verify="ca.crt")
client.get("https://api.example.com/")
Cost: cert distribution and rotation is operationally heavy — service meshes exist mostly to make this manageable.
Cipher suites and PFS
A cipher suite specifies key exchange + bulk encryption + MAC. TLS 1.3 stripped this down — only AEAD ciphers remain (AES-GCM, ChaCha20-Poly1305) and key exchange is always (EC)DHE for forward secrecy.
Forward secrecy means a future leak of the server’s long-term key doesn’t decrypt past traffic — each session uses an ephemeral DH key that’s discarded. Important for compliance and for resisting later attacks.
Common cert errors and what they mean
| Error | Meaning |
|---|---|
CERT_HAS_EXPIRED |
cert past notAfter. Renew. |
UNABLE_TO_VERIFY_LEAF_SIGNATURE |
missing intermediate cert in the chain |
HOSTNAME_MISMATCH |
cert is for foo.com, you connected to bar.com |
CERT_AUTHORITY_INVALID |
root CA not in client’s trust store (self-signed; or CA you didn’t add) |
SSL_ERROR_NO_CYPHER_OVERLAP |
client and server agreed on no cipher; usually too old TLS version on one side |
Python TLS gotchas
import requests
requests.get("https://self-signed.example/", verify=False) # disables validation — DON'T in prod
requests.get("https://api.example/", verify="/path/to/ca.crt") # use a private CA bundle
requestsships withcertifi(Mozilla CA bundle). On corp networks with MITM proxies, point it at the corporate CA bundle viaREQUESTS_CA_BUNDLEenv var.urllib3.disable_warnings(InsecureRequestWarning)silences the noise but leaves the security hole. Don’t.ssl.create_default_context()is the right starting point for raw socket TLS.- Old Python on old systems may default to TLS 1.0 — explicitly set
ssl.TLSVersion.TLSv1_2minimum.
HSTS — force HTTPS
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
Tells the browser “always use HTTPS for this domain for the next year.” Prevents downgrade attacks. preload opts into the browser’s built-in list (irreversible — think before adding).
What gets encrypted, what doesn’t
Encrypted: HTTP request line, headers, body. Cookies, auth tokens. Whatever’s in the TLS payload.
Visible to observers: source/destination IPs and ports, packet sizes and timing, SNI hostname (without ECH), the fact that you connected.
So TLS hides what you’re saying, but a network observer still knows you talked to api.example.com (and roughly how much). For metadata privacy you need Tor or similar.
Common interview confusions
- “SSL is the same as TLS.” — SSL 1/2/3 are the historical predecessors; TLS 1.0+ is the modern protocol. People still say “SSL” loosely. Correct vocabulary: TLS.
- “HTTPS encrypts the URL.” — encrypts the path and query (those are inside the HTTP request). Doesn’t encrypt the destination IP/port or the SNI hostname.
- “A wildcard cert
*.example.comcoversexample.com.” — no. Wildcards cover one subdomain level.*.example.comdoesn’t matchexample.comitself ora.b.example.com. Use a SAN cert with both names. - “TLS proves the server is the right company.” — DV proves they control the domain, nothing more. EV/OV add organization checks. Most attacks don’t need to forge a cert; they redirect DNS or phish.
Interview angle
- “What does TLS provide?” — confidentiality, integrity, server authentication (and client auth in mTLS). Not authorization, not metadata privacy.
- “Walk through the TLS 1.3 handshake.” — ClientHello (with key share, SNI, cipher list) → ServerHello + cert + Finished → Client Finished + app data. 1 RTT, 0 RTT for resumption.
- “What’s the cert chain and what’s a common misconfiguration?” — leaf → intermediate → root; root pre-trusted by OS/browser. Common miss: server doesn’t include intermediates, browsers fetch them but
curl/requestsfail withunable to verify leaf signature. - “What’s mTLS and when do you use it?” — both sides present certs. Service-to-service inside a mesh, high-security APIs, replacing static API keys with crypto identity. Cost is cert distribution / rotation — service meshes solve that.
- “What’s HSTS and what does
preloadmean?” — header telling browsers to always use HTTPS for this domain.preloadadds you to the browser’s hardcoded list — irreversible-ish; opt in with care. - “Let’s Encrypt and ACME — how does it work?” — automated DV cert issuance via HTTP-01 or DNS-01 challenge. 90-day certs auto-renewed. Caddy/Traefik do it built-in; nginx pairs with certbot.