TLS / HTTPS
TLS encrypts + authenticates connections between client and server. Modern TLS 1.3 is fast, simpler, secure by default. The piece you actually configure: ciphers, certs, HSTS, mutual TLS.
TLS 1.3, certificates, HSTS, mTLS
EXAMPLE
# 1) TLS in brief
# Two parties exchange:
# 1. Cipher suite negotiation (TLS 1.3 simplified this)
# 2. Server certificate (signed by a CA the client trusts)
# 3. Key exchange (ECDHE — ephemeral, forward-secret)
# 4. Symmetric session key — used to encrypt the rest
#
# Result: authenticated channel, confidential, integrity-protected.
# 2) nginx — modern HTTPS config (TLS 1.2 + 1.3)
server {
listen 443 ssl http2;
server_name app.example.com;
ssl_certificate /etc/letsencrypt/live/app.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/app.example.com/privkey.pem;
# Protocols
ssl_protocols TLSv1.2 TLSv1.3;
# Cipher suites — Mozilla 'intermediate' (broad compat)
ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384';
ssl_prefer_server_ciphers on;
# Session cache + tickets
ssl_session_cache shared:SSL:50m;
ssl_session_timeout 4h;
ssl_session_tickets off; # rotated tickets are tricky; off is safe default
# OCSP stapling (faster client validation)
ssl_stapling on;
ssl_stapling_verify on;
resolver 1.1.1.1 8.8.8.8 valid=60s;
# HSTS — force HTTPS for 1 year
add_header Strict-Transport-Security 'max-age=31536000; includeSubDomains; preload' always;
location / { proxy_pass http://app:3000; }
}
# 3) Free certificates — Let's Encrypt (certbot)
sudo certbot --nginx -d app.example.com -d www.app.example.com
# Auto-renewal:
sudo systemctl enable --now certbot.timer
# Wildcard via DNS-01 challenge
sudo certbot certonly --manual --preferred-challenges dns -d '*.example.com'
# Or with provider plugin (Route53, Cloudflare, etc.) for automation
# 4) Cert-manager (Kubernetes) — automated certs via Let's Encrypt
# Install cert-manager + define a ClusterIssuer + annotate Ingress.
# Result: automatic issuance + renewal, declarative.
# 5) Test your TLS configuration
# - https://www.ssllabs.com/ssltest/ — comprehensive grade A-F
# - testssl.sh — local CLI scanner
# - https://observatory.mozilla.org — broader security audit
# 6) Modern Node.js HTTPS server
import https from 'node:https';
import fs from 'node:fs';
const server = https.createServer({
key: fs.readFileSync('./server.key'),
cert: fs.readFileSync('./server.crt'),
ca: fs.readFileSync('./chain.crt'), // intermediate certs
minVersion: 'TLSv1.2',
maxVersion: 'TLSv1.3',
}, app);
server.listen(443);
# 7) Client — fetch HTTPS in Node
const response = await fetch('https://api.example.com/data');
# Verifies cert by default via system trust store.
# Override trust store (mTLS, self-signed in dev)
import { Agent } from 'undici';
const agent = new Agent({
connect: {
ca: fs.readFileSync('./ca.crt'),
cert: fs.readFileSync('./client.crt'),
key: fs.readFileSync('./client.key'),
},
});
await fetch('https://internal.example.com/', { dispatcher: agent });
# 8) curl with mTLS
curl --cacert ca.crt --cert client.crt --key client.key https://api.example.com
# 9) Mutual TLS (mTLS)
# Both server AND client present + verify certs.
# Use cases:
# - Service-to-service in zero-trust networks
# - Sensitive APIs (payment, healthcare)
# - Industrial / IoT device authentication
# nginx — require client cert
ssl_client_certificate /etc/ssl/client-ca.crt;
ssl_verify_client on;
ssl_verify_depth 2;
location /api {
# Read client cert info
proxy_set_header X-Client-DN $ssl_client_s_dn;
proxy_set_header X-Client-Verify $ssl_client_verify;
proxy_pass http://app:3000;
}
# 10) HSTS — HTTP Strict Transport Security
# Tells browsers: 'Always use HTTPS for this domain; never HTTP'
#
# Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
#
# preload — submit to https://hstspreload.org for browser-baked-in HSTS.
# Once preloaded, even FIRST visit is HTTPS-only.
# WARNING: hard to undo. Test extensively first.
# 11) Common misconfigurations
# ❌ Weak ciphers: 3DES, RC4, MD5, SHA1
# ❌ TLS 1.0 / 1.1 enabled (deprecated; PCI requires TLS 1.2+)
# ❌ Self-signed certs in production
# ❌ Expired certs (monitor!)
# ❌ Mismatched CN vs hostname
# ❌ HSTS without first ensuring HTTPS works everywhere
# ❌ HTTPS-only header without redirecting HTTP → HTTPS
# ❌ Wildcard cert reused across teams without rotation
# 12) Modern best practices (2025+)
# • TLS 1.3 + 1.2 (drop 1.0 + 1.1)
# • ECDHE + AEAD ciphers (GCM, ChaCha20-Poly1305)
# • ECDSA P-256 or P-384 certs (smaller, faster than RSA)
# • OCSP stapling enabled
# • HSTS with preload
# • HTTP/2 + HTTP/3 (QUIC) for performance
# • Forward secrecy ALWAYS (ECDHE — never plain RSA key exchange)
# • Certificate transparency (CT) — monitor logs (Cert Spotter, crt.sh)
# • Automatic cert rotation (cert-manager, certbot)
# 13) Performance tips
# • TLS 1.3 handshake = 1 RTT (vs 2 in 1.2)
# • Session resumption (tickets / cache) further reduces RTTs
# • HTTP/2 + 3 reduce connection overhead
# • CDN at the edge handles TLS termination — origin sees plaintext (or mTLS internally)
# • Hardware acceleration (AES-NI) on modern CPUs — TLS is essentially free
# 14) Cert renewal monitoring
# - Alert 30 days before expiry
# - Alert 7 days before expiry (urgent)
# - Alert on revocation
# Use: Datadog SSL checks, Pingdom, UptimeRobot, cert-monitor.com
# 15) Certificate Transparency (CT)
# All certs issued by trusted CAs are logged publicly.
# Monitor for unexpected certs in YOUR name:
# - crt.sh — search by domain
# - Cert Spotter — alerts on new certs for your domains
# Why: detect rogue CA issuance / phishing infrastructure setup
# 16) Post-quantum readiness
# NIST has selected: ML-KEM (Kyber) for KEM, ML-DSA (Dilithium) for signatures
# Hybrid key exchange (X25519 + ML-KEM) rolling out in TLS 1.3
# Plan: migrate long-term-signed artifacts (PDFs, contracts) first
# 17) Trust store
# What CAs your system trusts: /etc/ssl/certs (Linux), Keychain (macOS), Windows certificate store
# Don't add untrusted CAs — every cert that CA issues becomes valid for you
# Audit periodically
# 18) Common pen-test findings
# • SSLv3 / TLS 1.0 still enabled
# • Weak ciphers (3DES, RC4)
# • Self-signed or expired internal certs
# • Cert valid for wildcard but app only uses one host (over-broad)
# • Missing HSTS (browser may downgrade silently)
# • Heartbleed (CVE-2014-0160) — old OpenSSL still on perimeter devices
# • CRIME / BREACH compression attacks — TLS compression should be off (default in 1.3)
# • Predictable cookie / session token — orthogonal to TLS but often paired
# 19) Best practices summary
# ✅ TLS 1.3 + 1.2 only; ECDHE + AEAD
# ✅ Let's Encrypt or ACME-based cert issuance
# ✅ Cert auto-renewal; expiry alerts
# ✅ HSTS + preload (only after thorough testing)
# ✅ OCSP stapling
# ✅ mTLS for service-to-service in zero-trust
# ✅ Block weak ciphers in CI / runtime config
# ✅ Monitor CT logs for your domains
# ✅ Periodic external scans (SSL Labs A+ target)
# ✅ Plan for post-quantum migration
Why it matters
Let’s Encrypt + TLS 1.3 + HSTS + modern ciphers is the modern web’s default. Pair with cert-manager (Kubernetes) or certbot (servers) for automatic renewal. Aim for SSL Labs A+ — the misconfig costs less than a minute to fix.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
// Run TLS 1.3 only. Auto-renew via certbot / cert-manager / ACME. // Pin via DNS CAA records. HSTS preload your apex domain. // Test config at ssllabs.com or testssl.sh.Try it Yourself »
Discussion
Loading…