TLS & HTTPS
HTTPS = HTTP + TLS (Transport Layer Security). TLS provides three guarantees: confidentiality (encryption prevents eavesdropping), integrity (tampering is detected), and authentication (you are talking to the right server). Every production site must use HTTPS.
TLS Handshake (TLS 1.3)
# TLS 1.3 Handshake (1-RTT, simplified):
#
# Client → Server: ClientHello
# - TLS version: 1.3
# - Cipher suites: TLS_AES_256_GCM_SHA384, TLS_CHACHA20_POLY1305_SHA256
# - Client random (32 bytes)
# - Key share (Diffie-Hellman public key)
#
# Server → Client: ServerHello + Certificate + Finished
# - Chosen cipher suite
# - Server random
# - Server DH public key
# - Certificate chain (signed by trusted CA)
# - Finished MAC (proves handshake integrity)
#
# Client → Server: Finished
# - Verifies certificate against trusted CA roots
# - Both sides derive session keys from DH exchange
# - Encrypted application data begins
#
# 0-RTT (resumption): client can send data with first packet (replay risk)
# Inspect TLS details
openssl s_client -connect example.com:443 -tls1_3 2>&1 | head -40
curl -v https://example.com 2>&1 | grep -E "TLSv|cipher|certificate"
# Check certificate info
openssl s_client -connect example.com:443 2>/dev/null | openssl x509 -noout -dates -subject -issuer
# Check TLS version support
nmap --script ssl-enum-ciphers -p 443 example.com
# Decode a certificate file
openssl x509 -in cert.pem -text -nooutCertificates: Types & Lifecycle
# Certificate types by validation level:
# DV (Domain Validation) - CA verifies domain ownership only
# - Let's Encrypt (free), ZeroSSL. Issued in minutes.
# - OK for most sites and APIs
#
# OV (Organization Validation) - CA verifies org identity
# - Shows org name in certificate details
# - Mid-tier price, takes days
#
# EV (Extended Validation) - Rigorous org identity verification
# - Was shown as green address bar (browsers removed this)
# - Expensive, takes weeks
# Let's Encrypt with Certbot (free, auto-renews every 90 days)
sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d example.com -d www.example.com
sudo certbot renew --dry-run
# Auto-renewal cron
0 12 * * * /usr/bin/certbot renew --quiet
# Generate self-signed cert for development
openssl req -x509 -nodes -newkey rsa:2048 -days 365 \
-keyout dev.key -out dev.crt \
-subj "/CN=localhost" \
-addext "subjectAltName=DNS:localhost,IP:127.0.0.1"
# Generate CSR (Certificate Signing Request) for commercial CA
openssl genrsa -out private.key 2048
openssl req -new -key private.key -out request.csr
# Submit request.csr to CA, receive cert.crt back
# Convert formats
openssl pkcs12 -export -out cert.pfx -inkey private.key -in cert.crt # PEM → PFX
openssl pkcs12 -in cert.pfx -out cert.pem -nodes # PFX → PEMHSTS, Mixed Content & Pinning
# HSTS (HTTP Strict Transport Security)
# Tells browsers: ONLY connect over HTTPS for the next max-age seconds
# Header sent over HTTPS:
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
# max-age=31536000 = 1 year (recommended minimum)
# includeSubDomains = applies to all subdomains
# preload = eligible for browser preload list
# HSTS preload list: browsers ship with a hardcoded list of HSTS domains
# Submit at: https://hstspreload.org
# WARNING: once preloaded, it's very hard to remove - test first!
# Mixed content: page loaded over HTTPS includes HTTP resources
# Active mixed content (scripts, iframes) = BLOCKED by browsers
# Passive mixed content (images) = WARNING, may be blocked
# Fix: ensure all subresources use https:// or protocol-relative //
# Content Security Policy to prevent mixed content:
Content-Security-Policy: upgrade-insecure-requests
# Automatically upgrades HTTP sub-requests to HTTPS
# Certificate Pinning
# HTTP Public Key Pinning (HPKP) - deprecated, caused site outages
# Modern alternative: Expect-CT header + Certificate Transparency logs
Expect-CT: max-age=86400, enforce, report-uri="https://report.example.com/ct"
# Check HSTS and cert with:
curl -I https://example.com | grep -i "strict\|location"
ssl-checker.online # Free online tool
# nginx HTTPS config
server {
listen 443 ssl http2;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
ssl_prefer_server_ciphers off;
add_header Strict-Transport-Security "max-age=63072000" always;
}Keep your own version of these notes — editable, searchable, and organised by your stack.
Start free