Nginx SSL/TLS Configuration
Best practices for configuring SSL/TLS in nginx with modern security standards, cipher suites, and performance optimizations.
Basic HTTPS Configuration
Minimal HTTPS server block
Basic SSL/TLS configuration:
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name example.com;
ssl_certificate /etc/nginx/ssl/example.com.crt;
ssl_certificate_key /etc/nginx/ssl/example.com.key;
root /var/www/html;
index index.html index.htm;
location / {
try_files $uri $uri/ =404;
}
}HTTP to HTTPS redirect
Force all traffic to HTTPS:
server {
listen 80;
listen [::]:80;
server_name example.com;
return 301 https://$server_name$request_uri;
}Certificate chain
Include intermediate certificates:
# Concatenate certificate + intermediate chain
ssl_certificate /etc/nginx/ssl/example.com-fullchain.crt;
ssl_certificate_key /etc/nginx/ssl/example.com.key;
# Or build manually:
# cat example.com.crt intermediate.crt > fullchain.crtModern Security Configuration
TLS protocols
Enable only TLS 1.2 and 1.3:
# Modern configuration (TLS 1.2 and 1.3 only)
ssl_protocols TLSv1.2 TLSv1.3;
# If you must support older clients:
# ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;Modern cipher suites
Recommended cipher configuration for 2024+:
# Mozilla Modern profile (TLS 1.3 only)
ssl_protocols TLSv1.3;
ssl_prefer_server_ciphers off;
# Mozilla Intermediate profile (TLS 1.2 + 1.3)
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384';
ssl_prefer_server_ciphers off;Diffie-Hellman parameters
Generate and use strong DH parameters:
# Generate DH params (one-time setup):
# openssl dhparam -out /etc/nginx/dhparam.pem 4096
ssl_dhparam /etc/nginx/dhparam.pem;HSTS and Security Headers
HTTP Strict Transport Security
Force HTTPS for all future requests:
# HSTS with 2-year max-age, includeSubDomains, and preload
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;Complete security headers
Comprehensive security header configuration:
# HSTS
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
# Prevent MIME type sniffing
add_header X-Content-Type-Options "nosniff" always;
# XSS Protection (legacy browsers)
add_header X-XSS-Protection "1; mode=block" always;
# Clickjacking protection
add_header X-Frame-Options "SAMEORIGIN" always;
# Referrer policy
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# Content Security Policy (customize for your site)
add_header Content-Security-Policy "default-src 'self' https:; script-src 'self'; style-src 'self' 'unsafe-inline'" always;
# Permissions Policy (formerly Feature-Policy)
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;OCSP Stapling
Enable OCSP stapling
Improve performance and privacy:
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/nginx/ssl/ca-chain.crt;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;Test OCSP stapling
Verify OCSP stapling is working:
# Using OpenSSL
echo QUIT | openssl s_client -connect example.com:443 -status 2>&1 | grep -A 17 'OCSP response:'
# Should show: OCSP Response Status: successful (0x0)SSL Session Management
Session cache optimization
Improve performance with session caching:
# Session cache for faster reconnections
ssl_session_cache shared:SSL:50m;
ssl_session_timeout 1d;
ssl_session_tickets off; # Disable for forward secrecySession tickets (alternative)
If session tickets are needed, rotate keys:
# Generate session ticket keys:
# openssl rand 80 > /etc/nginx/ssl/ticket.key
ssl_session_tickets on;
ssl_session_ticket_key /etc/nginx/ssl/ticket.key;
# Rotate keys periodically (e.g., daily) for forward secrecyClient Certificate Authentication (mTLS)
Require client certificates
Enable mutual TLS authentication:
ssl_client_certificate /etc/nginx/ssl/client-ca.crt;
ssl_verify_client on;
ssl_verify_depth 2;Optional client certificates
Make client certs optional, check in application:
ssl_client_certificate /etc/nginx/ssl/client-ca.crt;
ssl_verify_client optional;
# Pass client cert info to application
location / {
proxy_set_header X-Client-Cert $ssl_client_cert;
proxy_set_header X-Client-Verify $ssl_client_verify;
proxy_pass http://backend;
}CRL checking
Validate client certificates against CRL:
ssl_client_certificate /etc/nginx/ssl/client-ca.crt;
ssl_crl /etc/nginx/ssl/ca-crl.pem;
ssl_verify_client on;SNI and Multiple Certificates
Multiple server blocks
Different certificates for different domains:
server {
listen 443 ssl;
server_name example.com www.example.com;
ssl_certificate /etc/nginx/ssl/example.com.crt;
ssl_certificate_key /etc/nginx/ssl/example.com.key;
# ... site config
}
server {
listen 443 ssl;
server_name api.example.com;
ssl_certificate /etc/nginx/ssl/api.example.com.crt;
ssl_certificate_key /etc/nginx/ssl/api.example.com.key;
# ... API config
}Wildcard certificate
Single certificate for all subdomains:
# Wildcard cert covers *.example.com
server {
listen 443 ssl;
server_name ~^(?<subdomain>.+)\.example\.com$;
ssl_certificate /etc/nginx/ssl/wildcard.example.com.crt;
ssl_certificate_key /etc/nginx/ssl/wildcard.example.com.key;
# Route based on subdomain
root /var/www/$subdomain;
}Complete Production Configuration
Hardened nginx SSL config
Complete example with all best practices:
# HTTP redirect to HTTPS
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
return 301 https://example.com$request_uri;
}
# HTTPS server
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name example.com www.example.com;
# SSL Certificate
ssl_certificate /etc/nginx/ssl/example.com-fullchain.crt;
ssl_certificate_key /etc/nginx/ssl/example.com.key;
# SSL Protocols and Ciphers
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305';
ssl_prefer_server_ciphers off;
# DH Parameters
ssl_dhparam /etc/nginx/dhparam.pem;
# SSL Session
ssl_session_cache shared:SSL:50m;
ssl_session_timeout 1d;
ssl_session_tickets off;
# OCSP Stapling
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/nginx/ssl/ca-chain.crt;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
# Security Headers
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# Logging
access_log /var/log/nginx/example.com.access.log;
error_log /var/log/nginx/example.com.error.log;
# Document Root
root /var/www/example.com;
index index.html index.htm;
# Routes
location / {
try_files $uri $uri/ =404;
}
}Performance Optimization
HTTP/2 and HTTP/3
Enable modern protocols for better performance:
# HTTP/2 (nginx 1.9.5+)
listen 443 ssl http2;
# HTTP/3 (nginx 1.25.0+, requires --with-http_v3_module)
listen 443 quic reuseport;
listen 443 ssl;
http3 on;
add_header Alt-Svc 'h3=":443"; ma=86400' always;SSL buffer tuning
Optimize buffer sizes for performance:
# Optimize SSL buffer size (default 16k)
ssl_buffer_size 4k; # Reduce latency for small responses
# For high-bandwidth transfers, increase buffer
# ssl_buffer_size 16k;Testing and Validation
Test configuration
Validate nginx config before reload:
# Test configuration syntax
nginx -t
# Reload if successful
nginx -s reload
# Or restart
systemctl restart nginxSSL Labs test
Comprehensive SSL configuration analysis:
# Visit: https://www.ssllabs.com/ssltest/
# Enter your domain for comprehensive analysis
# Target: A+ rating with modern configurationCheck TLS version
Verify enabled TLS protocols:
# Test TLS 1.2
openssl s_client -connect example.com:443 -tls1_2 < /dev/null
# Test TLS 1.3
openssl s_client -connect example.com:443 -tls1_3 < /dev/null
# Verify TLS 1.0/1.1 are disabled (should fail)
openssl s_client -connect example.com:443 -tls1 < /dev/nullImportant Notes
SSL directives can be in http, server, or location contexts. Put shared settings in http block, override in server blocks as needed.
Always test configuration changes with `nginx -t` before reloading. Invalid config can take down your entire site.
When updating certificates (renewal), just reload nginx (`nginx -s reload`) - no need for full restart.
Adding "preload" to HSTS header allows submission to browsers' HSTS preload list. This is permanent - carefully consider before enabling.
Session cache provides better forward secrecy. Disable session tickets unless you have specific needs and rotate ticket keys frequently.
Cipher recommendations change over time. Check Mozilla SSL Configuration Generator regularly for updates.
Private keys should be readable only by nginx user (e.g., chmod 600, chown www-data). Never world-readable.
Official nginx SSL module docs: nginx.org/en/docs/http/ngx_http_ssl_module.html
Mozilla SSL Config Generator: ssl-config.mozilla.org