Many administrators encounter a puzzling situation where Nginx continues to accept TLS 1.0/1.1 connections despite explicit configuration to allow only TLS 1.2. This behavior typically stems from either misconfiguration or version-specific quirks in the OpenSSL/Nginx interaction.
Your current configuration looks technically correct at first glance:
ssl_protocols TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_ciphers ECDH+AESGCM256:DH+AESGCM256:ECDH+AES256:!aNULL:!MD5:!kEDH;
Several factors could be causing this behavior:
- Version mismatch: Older Nginx versions (especially pre-1.9.1) may require different syntax
- Configuration inheritance: Check for multiple ssl_protocols directives in included files
- OpenSSL limitations: Some OpenSSL versions don't properly honor protocol restrictions
For Nginx 1.7.10 with OpenSSL 1.0.1f, use this definitive configuration:
ssl_protocols TLSv1.2;
ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256';
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
ssl_session_tickets off;
ssl_stapling on;
ssl_stapling_verify on;
After applying changes, verify with:
openssl s_client -connect yourdomain.com:443 -tls1
openssl s_client -connect yourdomain.com:443 -tls1_1
openssl s_client -connect yourdomain.com:443 -tls1_2
Only the TLS 1.2 test should succeed with a valid certificate chain.
For current Nginx versions (1.19.4+), consider:
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256;
ssl_ecdh_curve X25519:secp521r1:secp384r1;
ssl_prefer_server_ciphers on;
When configuring Nginx to use only TLS 1.2, many administrators encounter a frustrating situation where older protocols remain available despite explicit configuration. Here's what's happening under the hood:
# Common but problematic configuration
ssl_protocols TLSv1.2;
ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384';
The issue typically stems from three potential sources:
- OpenSSL version dependencies (1.0.1 series has known limitations)
- Configuration inheritance in Nginx server blocks
- Missing server restarts or configuration reloads
Here's a verified configuration that properly enforces TLS 1.2:
server {
listen 443 ssl;
server_name example.com;
# SSL Configuration
ssl_protocols TLSv1.2;
ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305';
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
# Security headers
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload";
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
# Certificate paths
ssl_certificate /etc/ssl/certs/example.com.crt;
ssl_certificate_key /etc/ssl/private/example.com.key;
}
After implementing the configuration, verify using these tools:
# Using OpenSSL client
openssl s_client -connect example.com:443 -tls1
openssl s_client -connect example.com:443 -tls1_1
openssl s_client -connect example.com:443 -tls1_2
# Using online tools
curl -v https://example.com --tlsv1.2
curl -v https://example.com --tlsv1.1
Problem: Configuration appears correct but old protocols still work
Solution: Check for multiple ssl_protocols directives in included files
Problem: OpenSSL version limitations
Solution: Upgrade to OpenSSL 1.1.0+ where possible
# Check OpenSSL version
openssl version
While enforcing TLS 1.2 improves security, consider these optimizations:
- Enable OCSP stapling to reduce handshake time
- Implement session tickets for better performance
- Consider TLS 1.3 for modern clients
# OCSP Stapling configuration
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;