How TLS Works
TLS (Transport Layer Security) encrypts the connection between browser and server. HTTPS is simply HTTP running over TLS. The process involves:
- Handshake — Client and server agree on encryption parameters
- Certificate validation — Client verifies server identity
- Key exchange — Establish shared secret for encryption
- Encrypted communication — All HTTP traffic is encrypted
Client Server │ │ │──────── ClientHello (supported ciphers) ─────►│ │ │ │◄─────── ServerHello (chosen cipher) ──────────│ │◄─────── Certificate ──────────────────────────│ │◄─────── ServerHelloDone ──────────────────────│ │ │ │ [Client verifies certificate chain] │ │ │ │──────── ClientKeyExchange ───────────────────►│ │──────── ChangeCipherSpec ────────────────────►│ │──────── Finished ────────────────────────────►│ │ │ │◄─────── ChangeCipherSpec ─────────────────────│ │◄─────── Finished ─────────────────────────────│ │ │ │◄════════ Encrypted HTTP Traffic ═════════════►│
Certificate Fundamentals
A TLS certificate contains:
- Public key — Used to encrypt data and verify signatures
- Domain name(s) — Which domains this certificate is valid for
- Issuer — Who signed/vouched for this certificate
- Validity period — Start and expiration dates
- Signature — Cryptographic proof from the issuer
The Certificate Chain
Browsers trust a set of root certificates (Certificate Authorities). Your server's certificate is signed by an intermediate CA, which is signed by a root CA:
┌─────────────────────────────────────────┐
│ Root CA (in browser) │ ← Trusted by default
│ e.g., DigiCert Root │
└───────────────────┬─────────────────────┘
│ signs
▼
┌─────────────────────────────────────────┐
│ Intermediate CA │ ← Your server sends this
│ e.g., DigiCert SHA2 Extended │
└───────────────────┬─────────────────────┘
│ signs
▼
┌─────────────────────────────────────────┐
│ Your Certificate │ ← Your server sends this
│ example.com │
└─────────────────────────────────────────┘
Your server must send both its certificate AND intermediate certificate(s). Missing intermediates cause "certificate not trusted" errors.
Let's Encrypt and Certbot
Let's Encrypt provides free, automated TLS certificates. Certbot handles obtaining and renewing certificates automatically.
# Install Certbot (Ubuntu/Debian)
sudo apt update
sudo apt install certbot python3-certbot-nginx
# Obtain certificate and configure Nginx automatically
sudo certbot --nginx -d example.com -d www.example.com
# Or just get the certificate (manual config)
sudo certbot certonly --webroot -w /var/www/html -d example.com
# Test automatic renewal
sudo certbot renew --dry-run
# Certificates are stored in:
# /etc/letsencrypt/live/example.com/fullchain.pem (cert + intermediates)
# /etc/letsencrypt/live/example.com/privkey.pem (private key)Automatic renewal: Certbot installs a systemd timer or cron job that runs twice daily, checking if any certificates need renewal (they renew when less than 30 days remain). Let's Encrypt certificates are valid for 90 days.
Nginx TLS Configuration
server {
listen 443 ssl http2;
server_name example.com;
# Certificate files
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
# TLS versions (disable old, insecure versions)
ssl_protocols TLSv1.2 TLSv1.3;
# Cipher suites (modern, secure selection)
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
# Session caching (reduces handshake overhead)
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
# OCSP stapling (faster certificate validation)
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4 valid=300s;
# ... rest of server config
}HTTP to HTTPS Redirect
Force all traffic to use HTTPS:
# Redirect all HTTP to HTTPS
server {
listen 80;
server_name example.com www.example.com;
# Allow Let's Encrypt challenges
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
# Redirect everything else
location / {
return 301 https://$host$request_uri;
}
}HSTS: HTTP Strict Transport Security
HSTS tells browsers to always use HTTPS for your domain, even if
the user types http://:
# Add HSTS header (in HTTPS server block)
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;| Directive | Meaning |
|---|---|
max-age=31536000 |
Remember for 1 year |
includeSubDomains |
Apply to all subdomains too |
preload |
Request inclusion in browser preload lists |
HSTS is a commitment: Once browsers see your HSTS header,
they will refuse HTTP connections for the specified duration. If you later
need to serve HTTP (e.g., certificate problems), users won't be able to
access your site. Start with a short max-age (like 3600) and
increase gradually.
HSTS Preload
The preload list is hardcoded into browsers. Sites on this list use HTTPS from the very first visit, eliminating even the initial HTTP request:
# To qualify for preloading:
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
# Then submit at: https://hstspreload.orgCommon TLS Errors
| Error | Cause | Solution |
|---|---|---|
| Certificate expired | Renewal failed or forgotten | Renew certificate, fix auto-renewal |
| Certificate not trusted | Missing intermediate certificates | Use fullchain.pem, not just cert.pem |
| Name mismatch | Certificate for wrong domain | Obtain certificate for correct domain(s) |
| SSL_ERROR_RX_RECORD_TOO_LONG | Server sending HTTP on port 443 | Check ssl directive in server block |
| Connection reset | TLS version/cipher mismatch | Check ssl_protocols and ssl_ciphers |
Testing Your Configuration
# Check certificate details
openssl s_client -connect example.com:443 -servername example.com
# Check certificate chain
openssl s_client -connect example.com:443 -showcerts
# Verify certificate dates
echo | openssl s_client -connect example.com:443 2>/dev/null | openssl x509 -noout -dates
# Full SSL Labs analysis (web-based)
# https://www.ssllabs.com/ssltest/TLS Version Recommendations
| Version | Status | Recommendation |
|---|---|---|
| TLS 1.3 | Current, secure | Enable (preferred) |
| TLS 1.2 | Secure with right ciphers | Enable (for compatibility) |
| TLS 1.1 | Deprecated | Disable |
| TLS 1.0 | Insecure | Disable |
| SSL 3.0 | Broken (POODLE) | Disable |
Use Mozilla's generator: Don't write cipher suites by hand. Use Mozilla's SSL Configuration Generator to get current, tested configurations for your server and compatibility requirements.
What's Next
With TLS configured, you'll want to monitor your server's health. The next tutorial covers logging—what to log, log formats, and using logs for debugging and monitoring.