Why TLS Hardening Matters
Transport Layer Security (TLS) is the first line of defense for any public‑facing web service. A mis‑configured TLS stack can expose your site to downgrade attacks, weak cipher exploitation, and even data leakage. When you add HTTP/2 to the mix, you gain performance benefits—but only if the underlying TLS configuration is rock‑solid. This guide walks you through seven practical steps to lock down TLS and enable HTTP/2 on Nginx, targeting SREs who need a secure, high‑performance edge.
1. Enforce Modern Protocol Versions
Older protocols like SSLv3, TLS 1.0, and TLS 1.1 are considered insecure. Nginx lets you specify exactly which versions to negotiate.
ssl_protocols TLSv1.2 TLSv1.3; # Only allow TLS 1.2 and 1.3
ssl_prefer_server_ciphers on; # Server decides the cipher order
TLS 1.3 brings forward secrecy by default and reduces handshake latency, while TLS 1.2 remains necessary for older browsers that don’t yet support 1.3.
2. Curate a Strong Cipher Suite
A curated cipher list eliminates weak algorithms (e.g., DES, 3DES, RC4) and prefers AEAD ciphers with forward secrecy.
ssl_ciphers \
"TLS_AES_256_GCM_SHA384:TLS_AES_128_GCM_SHA256:TLS_CHACHA20_POLY1305_SHA256:" \
"ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:" \
"ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256";
The ordering matters: Nginx will pick the first mutually supported cipher. The list above mirrors Mozilla’s "Intermediate" recommendation, balancing compatibility and security.
3. Enable HTTP/2 Safely
HTTP/2 requires TLS on most browsers, and Nginx can enable it with a single directive. However, you should also turn off the server_tokens
directive to avoid leaking version information.
listen 443 ssl http2;
server_tokens off;
If you need to support HTTP/1.1 for legacy clients, Nginx will automatically fall back.
4. Harden Header Security
Security‑related headers add another layer of protection against click‑jacking, MIME sniffing, and mixed‑content issues.
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
add_header X-Content-Type-Options nosniff;
add_header X-Frame-Options DENY;
add_header Referrer-Policy "no-referrer-when-downgrade";
add_header Content-Security-Policy "default-src 'self'";
The HSTS header tells browsers to always use HTTPS for the next two years and signals readiness for the Chrome preload list.
5. Configure OCSP Stapling
OCSP stapling reduces latency and protects against certificate revocation attacks. It requires a valid ssl_trusted_certificate
that contains the full chain of your CA.
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/ssl/certs/ca-bundle.crt;
resolver 8.8.8.8 8.8.4.4 valid=300s; # Google's DNS for OCSP queries
With stapling enabled, the server presents the OCSP response during the TLS handshake, eliminating the extra round‑trip to the CA.
6. Optimize Session Resumption
TLS session tickets and session caches speed up repeat connections. For most production environments, enabling both is safe.
ssl_session_cache shared:SSL:10m; # Approx. 4000 sessions
ssl_session_timeout 1d;
ssl_session_tickets on; # Enable tickets (default in modern Nginx)
If you have strict compliance requirements, you can disable tickets and rely solely on server‑side caches.
7. Test, Monitor, and Automate Renewal
A hardened configuration is only useful if you know it works. Use tools like Qualys SSL Labs, testssl.sh, or openssl s_client to verify protocol and cipher support.
# Quick OpenSSL sanity check
openssl s_client -connect example.com:443 -tls1_3 -servername example.com
Set up automated monitoring with Prometheus + node_exporter to alert on TLS handshake failures or expired certificates. Combine this with certbot or acme.sh for zero‑downtime renewal.
# Example certbot renewal hook that reloads Nginx
#!/bin/sh
/usr/sbin/nginx -s reload
Putting It All Together
Below is a minimal yet production‑ready server block that incorporates the seven tips:
server {
listen 443 ssl http2;
server_name www.example.com;
# TLS basics
ssl_certificate /etc/ssl/certs/example.com.crt;
ssl_certificate_key /etc/ssl/private/example.com.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers "TLS_AES_256_GCM_SHA384:TLS_AES_128_GCM_SHA256:TLS_CHACHA20_POLY1305_SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256";
# Session handling
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_session_tickets on;
# OCSP stapling
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/ssl/certs/ca-bundle.crt;
resolver 8.8.8.8 8.8.4.4 valid=300s;
# Security headers
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
add_header X-Content-Type-Options nosniff;
add_header X-Frame-Options DENY;
add_header Referrer-Policy "no-referrer-when-downgrade";
add_header Content-Security-Policy "default-src 'self'";
server_tokens off;
# Application root
root /var/www/html;
index index.html index.htm;
}
Deploy the block, reload Nginx (nginx -s reload
), and run a quick SSL Labs scan. You should see an "A+" rating with no protocol or cipher warnings.
Final Thoughts
Hardening TLS and enabling HTTP/2 on Nginx is a low‑effort, high‑impact investment for any production site. By following these seven steps you gain stronger encryption, faster handshakes, and better visibility into potential misconfigurations. Remember to treat TLS as a living component: schedule regular scans, keep your OpenSSL library up to date, and automate certificate renewals. For deeper dives into Nginx performance tuning and real‑world case studies, check out https://lacidaweb.com for practical guides and community insights.
Top comments (0)