Cipher Suites Security Guide: TLS 1.2 to TLS 1.3 Configuration
Your cipher suite configuration is the backbone of your website's encryption security. Get it wrong, and you're vulnerable to attacks. Get it right, and you earn user trust and better SEO rankings.
This guide walks you through secure cipher suite configuration for every major platform, with ready-to-use configuration snippets and troubleshooting tips.
Understanding Cipher Suite Security
What Makes a Cipher Suite Secure?
A secure cipher suite must provide:
- Forward Secrecy (FS): Sessions can't be decrypted later even if the private key is compromised
- Strong Encryption: At minimum AES-128 or ChaCha20
- Authenticated Encryption: Uses GCM or ChaCha20-Poly1305 modes
- No Known Vulnerabilities: Avoids RC4, DES, MD5, SHA1
Security Tier Classification
| Tier | Cipher Suites | Use Case |
|---|---|---|
| A+ | TLS 1.3 only (5 suites) | Maximum security, modern clients |
| A | TLS 1.3 + TLS 1.2 (5-8 suites) | Best compatibility + security |
| B | TLS 1.2 with legacy ciphers | Legacy browser support needed |
| C | Includes weak ciphers | Needs urgent upgrade |
TLS 1.3 Cipher Suites (Required for A+)
TLS 1.3 dramatically simplified cipher suite configuration. Only 5 suites are approved, and all are secure:
TLS 1.3 Cipher Suites
# TLS 1.3 only cipher suites (all secure, no configuration needed)
TLS_AES_256_GCM_SHA384 # AES-256-GCM with SHA-384
TLS_AES_128_GCM_SHA256 # AES-128-GCM with SHA-256 (recommended for performance)
TLS_CHACHA20_POLYPOLY1305_SHA256 # ChaCha20-Poly1305 (best for mobile)
TLS_AES_128_CCM_SHA256 # AES-128-CCM (IoT, low-power devices)
TLS_AES_128_CCM_8_SHA256 # AES-128-CCM-8 (very constrained devices)
Key point: TLS 1.3 cipher suites are non-negotiable—all 5 are secure. You don't need to manually configure them.
TLS 1.2 Cipher Suites (For Compatibility)
TLS 1.2 allows many cipher suites, including some that are insecure. You must carefully curate which ones to enable.
Recommended TLS 1.2 Cipher Suites
Priority 1: Modern, Forward Secret (Use These)
ECDHE-ECDSA-AES256-GCM-SHA384 # ECDHE forward secrecy, AES-256-GCM
ECDHE-ECDSA-AES128-GCM-SHA256 # ECDHE forward secrecy, AES-128-GCM (faster)
ECDHE-RSA-AES256-GCM-SHA384 # ECDHE forward secrecy, RSA auth, AES-256-GCM
ECDHE-RSA-AES128-GCM-SHA256 # ECDHE forward secrecy, RSA auth, AES-128-GCM
ECDHE-ECDSA-CHACHA20-POLY1305 # ECDHE forward secrecy, ChaCha20 (mobile)
ECDHE-RSA-CHACHA20-POLY1305 # ECDHE forward secrecy, RSA auth, ChaCha20
Priority 2: Legacy Compatibility (Optional)
ECDHE-ECDSA-AES256-SHA384 # CBC mode, SHA-384 (older systems)
ECDHE-ECDSA-AES128-SHA256 # CBC mode, SHA-256 (older systems)
ECDHE-RSA-AES256-SHA384 # CBC mode, SHA-384 (older systems)
ECDHE-RSA-AES128-SHA256 # CBC mode, SHA-256 (older systems)
Cipher Suites to Disable
Critical: Must Be Disabled
| Cipher Suite | Reason |
|---|---|
aNULL | No authentication |
eNULL | No encryption |
EXPORT | Intentionally weak (export compliance) |
RC4 | Broken cipher |
DES | 56-bit key (too weak) |
3DES | 64-bit effective (slow and weak) |
MD5 | Collision attacks |
SHA1 | Collision attacks |
Should Be Disabled (Deprecated)
| Cipher Suite | Reason |
|---|---|
CBC without GCM | BEAST attack risk |
AES128-SHA | No forward secrecy |
AES256-SHA | No forward secrecy |
DHE-RSA-AES128-SHA | Weak DH parameters |
Nginx Configuration
Production-Ready Nginx Configuration
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name yourdomain.com;
# Certificate files
ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
# TLS Configuration - Modern
ssl_protocols TLSv1.2 TLSv1.3;
# Cipher suites - TLS 1.3 (auto) + TLS 1.2 (curated)
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';
# Prefer server cipher order
ssl_prefer_server_ciphers on;
# ECDH curve for key exchange
ssl_ecdh_curve secp384r1;
# Session cache
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
ssl_session_tickets off;
# OCSP Stapling
ssl_stapling on;
ssl_stapling_verify on;
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;
# HSTS preload (submit to hstspreload.org)
}
# HTTP to HTTPS redirect
server {
listen 80;
listen [::]:80;
server_name yourdomain.com;
return 301 https://$host$request_uri;
}
Test Your Nginx Configuration
# Test configuration syntax
nginx -t
# Reload configuration
nginx -s reload
# Check which ciphers your server offers
openssl s_client -connect yourdomain.com:443 -cipher 'ECDHE-RSA-AES128-GCM-SHA256' < /dev/null
# Full cipher scan
nmap --script ssl-enum-ciphers -p 443 yourdomain.com
Apache Configuration
Production-Ready Apache Configuration
<VirtualHost *:443>
ServerName yourdomain.com
DocumentRoot /var/www/html
# Certificate files
SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/yourdomain.com/cert.pem
SSLCertificateKeyFile /etc/letsencrypt/live/yourdomain.com/privkey.pem
SSLCertificateChainFile /etc/letsencrypt/live/yourdomain.com/chain.pem
# TLS Configuration
SSLProtocol all -SSLv2 -SSLv3 -TLSv1 -TLSv1.1
# Cipher suites
SSLCipherSuite 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
SSLHonorCipherOrder on
# ECDH curve
SSLECDHCurve secp384r1
# Session cache
SSLUseStapling on
SSLStaplingResponderTimeout 5
SSLStaplingReturnResponderErrors off
# Security Headers
Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
Header always set X-Content-Type-Options "nosniff"
Header always set X-Frame-Options "SAMEORIGIN"
Header always set X-XSS-Protection "1; mode=block"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
</VirtualHost>
# HTTP to HTTPS redirect
<VirtualHost *:80>
ServerName yourdomain.com
Redirect permanent / https://yourdomain.com/
</VirtualHost>
Test Apache Configuration
# Test configuration
apachectl configtest
# Reload Apache
systemctl reload apache2
# Check cipher support
openssl s_client -connect yourdomain.com:443 < /dev/null 2>&1 | grep -i cipher
Cloud Platform Configuration
AWS Application Load Balancer (ALB)
# AWS CLI command to update ALB security policy
aws elbv2 modify-listener \
--listener-arn arn:aws:elasticloadbalancing:region:account:listener/app/youralb/123 \
--ssl-policy ELBSecurityPolicy-TLS13-1-2-2021-06 \
--certificates CertificateArn=arn:aws:acm:region:account:certificate/id
Available TLS policies (AWS):
| Policy | TLS 1.3 | TLS 1.2 | Notes |
|---|---|---|---|
ELBSecurityPolicy-TLS13-1-2-2021-06 | ✅ | ✅ | Recommended |
ELBSecurityPolicy-TLS13-1-1-2021-06 | ✅ | ✅ | Legacy compatibility |
ELBSecurityPolicy-TLS13-1-0-2021-06 | ✅ | ✅ | Older clients |
ELBSecurityPolicy-2016-08 | ❌ | ✅ | Legacy (avoid) |
Cloudflare
// Cloudflare SSL/TLS Settings -> Edge Certificates
// TLS versions: TLS 1.2 + TLS 1.3
// Minimum TLS version: TLS 1.2
// Cipher suites (automatic, but you can enable "TLS 1.3 Only")
// No manual configuration needed - Cloudflare handles this
Cloudflare recommended settings:
| Setting | Value |
|---|---|
| TLS versions | TLS 1.2, TLS 1.3 |
| Minimum TLS version | TLS 1.2 |
| TLS 1.3 | Enabled |
| Automatic HTTPS Rewrites | On |
| HSTS | Enabled (max-age: 12 months) |
Google Cloud Load Balancing
# gcloud compute ssl-policies create secure-tls-policy \
# --description "TLS 1.3 with strong cipher suites" \
# --min-tls-version 1.2 \
# --profile Modern
# Available profiles:
# - Modern: TLS 1.2-1.3, strong ciphers
# - Intermediate: TLS 1.0-1.3, more compatibility
# - Legacy: All TLS versions, weak ciphers (avoid)
Microsoft Azure
# Set-AzApplicationGatewaySslPolicy -PolicyType Predefined -PolicyName AppGwSslPolicy20220101S
Recommended Azure policies:
| Policy Name | TLS 1.3 | TLS 1.2 | Notes |
|---|---|---|---|
AppGwSslPolicy20220101S | ✅ | ✅ | Recommended |
AppGwSslPolicy20220101 | ❌ | ✅ | Legacy |
ELBSecurityPolicy-TLS12-1-2-2021-06 | ✅ | ✅ | If using Azure Front Door |
Testing Your Configuration
Quick Test Commands
# Test TLS 1.3 support
echo | openssl s_client -connect yourdomain.com:443 -tls1_3 2>&1 | grep "Protocol\|Cipher"
# Test TLS 1.2 support
echo | openssl s_client -connect yourdomain.com:443 -tls1_2 2>&1 | grep "Protocol\|Cipher"
# Check for weak ciphers
openssl s_client -connect yourdomain.com:443 </dev/null 2>/dev/null | openssl x509 -noout -text | grep -A 3 "Signature Algorithm"
# Test with SSL Labs (comprehensive)
# https://www.ssllabs.com/ssltest/analyze.html?d=yourdomain.com
SSL Labs Grading Criteria (2026)
| Grade | Requirements |
|---|---|
| A+ | TLS 1.3, HSTS preload, no vulnerabilities, 100% key exchange, 90%+ encryption |
| A | TLS 1.3, no vulnerabilities, 80%+ key exchange, 80%+ encryption |
| B | TLS 1.2+, minor issues, 60%+ key exchange |
| C | TLS 1.2, some weak ciphers |
| D-F | TLS 1.0/1.1, weak ciphers, vulnerabilities |
Common SSL Labs Warnings
| Warning | Cause | Fix |
|---|---|---|
| "Chain issues" | Missing intermediate cert | Install full certificate chain |
| "Strict Transport Security" | HSTS not enabled | Add HSTS header |
| "Secure Client-Initiated Renegotiation" | Renegotiation allowed | Disable with ssl_renegotiate |
| "Logjam" | Weak DH parameters | Use ECDH with secp384r1 or higher |
| "Sweet32" | 3DES cipher enabled | Remove 3DES from cipher list |
Troubleshooting Common Issues
Issue: Grade Dropped After Update
Symptom: Your SSL grade dropped from A to B after updating configuration.
Common causes:
- Removed CBC mode ciphers but server can't negotiate with some clients
- ECDH curve too high (secp521r1 not supported by all clients)
- Missing RSA key exchange ciphers for compatibility
Solution: Use secp384r1 for ECDH, keep 4-6 modern cipher suites.
Issue: Some Clients Can't Connect
Symptom: Older browsers or devices fail to connect.
Solution: Check what's failing:
# Test with specific cipher
openssl s_client -connect yourdomain.com:443 -cipher 'ECDHE-RSA-AES128-SHA' < /dev/null
# Add fallback cipher if needed
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256';
Issue: OCSP Stapling Not Working
Symptom: SSL Labs reports "OCSP stapling: No"
Solution:
# Verify OCSP is enabled
ssl_stapling on;
ssl_stapling_verify on;
# Check DNS resolver
resolver 8.8.8.8 8.8.4.4 valid=300s;
# Test OCSP response
openssl s_client -connect yourdomain.com:443 -servername yourdomain.com -status < /dev/null
Issue: Mixed Content Warnings
Symptom: HTTPS page loads HTTP resources.
Solution:
# Force HTTPS and fix mixed content
add_header Content-Security-Policy "upgrade-insecure-requests" always;
Automated Monitoring
Cipher Suite Health Check Script
#!/bin/bash
# ssl-health-check.sh
DOMAIN="yourdomain.com"
ALERT_EMAIL="[email protected]"
# Get SSL grade from SSL Labs API (simplified)
GRADE=$(curl -s "https://api.ssllabs.com/api/v3/analyze/${DOMAIN}" | jq -r '.endpoints[0].grade')
if [[ "$GRADE" == "A" || "$GRADE" == "A+" ]]; then
echo "SSL Grade: $GRADE ✓"
exit 0
else
echo "WARNING: SSL Grade is $GRADE (expected A or A+)" | mail -s "SSL Alert: $DOMAIN" $ALERT_EMAIL
exit 1
fi
Add to Cron
# Run daily health check
0 9 * * * /usr/local/bin/ssl-health-check.sh
Configuration Checklist
Before Deployment
- Backup current configuration
- Test in staging environment
- Verify TLS 1.3 works (openssl s_client)
- Verify TLS 1.2 fallback works
- Check SSL Labs grade
- Verify all subdomains work
After Deployment
- Test from multiple networks
- Verify mobile device connectivity
- Check server error logs for SSL errors
- Monitor for any connection failures
- Update monitoring alerts if needed
Ongoing Maintenance
- Review SSL Labs report weekly
- Update cipher lists quarterly
- Monitor TLS version usage
- Test new cipher suites before enabling
- Keep up with TLS 1.4 (when released)
TL;DR Quick Reference
Nginx One-Liner (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';
ssl_prefer_server_ciphers on;
ssl_ecdh_curve secp384r1;
Apache One-Liner (TLS 1.2 + 1.3)
SSLProtocol all -SSLv2 -SSLv3 -TLSv1 -TLSv1.1
SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384
SSLHonorCipherOrder on
SSLECDHCurve secp384r1
Golden Rules
- Always use TLS 1.3 when possible
- Enable TLS 1.2 for compatibility
- Disable TLS 1.0 and 1.1 (insecure)
- Disable SSL 2.0 and 3.0 (obsolete)
- Prefer GCM and ChaCha20 over CBC
- Use forward secrecy (ECDHE)
- Test with SSL Labs after any changes
Monitor Your Security
Proper configuration is just the beginning. Monitor your certificates and configuration continuously.
Check your cipher suite configuration with GuardSSL →
Instant analysis of your TLS setup, cipher suites, and security recommendations. Free and takes less than 5 seconds.
This guide was updated January 2026 to reflect the latest TLS standards and security best practices.
Check Your SSL Certificate Now
Want to see these certificate details for your own website? Use our free SSL checker to instantly analyze your certificate's security, validity, and configuration.
No registration required • Instant results • 100% free