TLS/HTTPS

Certificates, encryption, and secure configuration

How TLS Works

TLS (Transport Layer Security) encrypts the connection between browser and server. HTTPS is simply HTTP running over TLS. The process involves:

  1. Handshake — Client and server agree on encryption parameters
  2. Certificate validation — Client verifies server identity
  3. Key exchange — Establish shared secret for encryption
  4. 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 ═════════════►│
				
Simplified TLS 1.2 handshake (TLS 1.3 is faster)

Certificate Fundamentals

A TLS certificate contains:

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                      │
└─────────────────────────────────────────┘
				
Certificate chain of trust

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.org

Common 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.