TLS 1.3 Configuration and Optimization
TLS 1.3 is the latest encryption protocol providing improved security, reduced latency, and simplified configuration. This guide covers TLS 1.3 features, cipher suite selection, 0-RTT handling, session resumption, and optimization for Nginx and Apache.
Table of Contents
- Prerequisites
- TLS 1.3 Overview
- TLS 1.3 Cipher Suites
- Nginx TLS 1.3 Configuration
- Apache TLS 1.3 Configuration
- Session Resumption
- 0-RTT (Early Data)
- Key Share Configuration
- Performance Testing
- Compatibility Considerations
- Conclusion
Prerequisites
Before implementing TLS 1.3, ensure you have:
- OpenSSL 1.1.1+ installed
- Nginx 1.13+ or Apache 2.4.36+
- Linux kernel supporting TLS 1.3 (most modern kernels)
- Test client supporting TLS 1.3 (modern browsers, curl)
- Basic understanding of TLS concepts
TLS 1.3 Overview
Key improvements over TLS 1.2:
Faster handshake:
- Reduced round trips (1-RTT instead of 2-RTT)
- 0-RTT session resumption available
Simplified configuration:
- Only 5 cipher suites (vs. hundreds in TLS 1.2)
- Cipher suite names changed format
- Automatic key exchange with recommended curves
Better security:
- Removed weak algorithms (MD5, SHA-1, RC4)
- All cipher suites provide forward secrecy
- No downgrade attacks
Format changes:
- TLS 1.2:
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 - TLS 1.3:
TLS_AES_256_GCM_SHA384
TLS 1.3 Cipher Suites
TLS 1.3 specifies only 5 cipher suites:
TLS_AES_256_GCM_SHA384 (recommended, 256-bit symmetric)
TLS_CHACHA20_POLY1305_SHA256 (modern, stream cipher alternative)
TLS_AES_128_GCM_SHA256 (lighter, 128-bit symmetric)
TLS_AES_128_CCM_SHA256 (IoT devices)
TLS_AES_128_CCM_8_SHA256 (extremely resource-limited)
OpenSSL format (used in web servers):
TLS13-AES-256-GCM-SHA384
TLS13-CHACHA20-POLY1305-SHA256
TLS13-AES-128-GCM-SHA256
Recommended cipher suite order:
1. TLS13-AES-256-GCM-SHA384 (strongest, recommended)
2. TLS13-CHACHA20-POLY1305-SHA256 (mobile/modern)
3. TLS13-AES-128-GCM-SHA256 (fallback)
Nginx TLS 1.3 Configuration
Configure Nginx for TLS 1.3:
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name example.com;
# Certificates
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
# TLS 1.3 and 1.2 (deprecate 1.0 and 1.1)
ssl_protocols TLSv1.3 TLSv1.2;
# TLS 1.3 cipher suites
ssl_ciphers TLS13-AES-256-GCM-SHA384:TLS13-CHACHA20-POLY1305-SHA256:TLS13-AES-128-GCM-SHA256;
# TLS 1.2 cipher suites (fallback)
ssl_ciphers TLS13-AES-256-GCM-SHA384:TLS13-CHACHA20-POLY1305-SHA256:TLS13-AES-128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
# Prefer server cipher order
ssl_prefer_server_ciphers on;
# Session resumption
ssl_session_cache shared:SSL:50m;
ssl_session_timeout 1d;
# OCSP stapling
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
resolver 8.8.8.8 8.8.4.4;
# HSTS header
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
}
Verify TLS 1.3 support:
# Check OpenSSL version
openssl version
# Test TLS 1.3 connection
openssl s_client -tls1_3 -connect example.com:443
Apache TLS 1.3 Configuration
Configure Apache for TLS 1.3:
<VirtualHost *:443>
ServerName example.com
SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/example.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem
# TLS versions
SSLProtocol TLSv1.3 TLSv1.2
# TLS 1.3 cipher suites
SSLCipherSuite TLS13-AES-256-GCM-SHA384:TLS13-CHACHA20-POLY1305-SHA256:TLS13-AES-128-GCM-SHA256
# Combined with TLS 1.2 ciphers
SSLCipherSuite TLS13-AES-256-GCM-SHA384:TLS13-CHACHA20-POLY1305-SHA256:TLS13-AES-128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384
# Server controls cipher order
SSLHonorCipherOrder on
# Session cache
SSLSessionCache shmcb:/var/run/apache2/ssl_scache(512000)
SSLSessionCacheTimeout 300
# OCSP stapling
SSLUseStapling on
SSLStaplingCache shmcb:/var/run/apache2/ocsp_cache(128000)
# HSTS header
Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
</VirtualHost>
Enable required Apache modules:
sudo a2enmod mod_ssl
sudo a2enmod mod_socache_shmcb
sudo a2enmod mod_headers
sudo systemctl reload apache2
Verify Apache TLS 1.3:
apache2ctl -M | grep ssl
openssl s_client -tls1_3 -connect example.com:443
Session Resumption
TLS 1.3 supports session resumption via session IDs and session tickets.
Session IDs: Traditional resumption mechanism
- Server stores session state
- Client sends session ID on reconnection
- Stateful, requires server-side storage
Session Tickets: Modern resumption (RFC 5077)
- Server encrypts session data, sends to client
- Client includes ticket on reconnection
- Stateless, lighter weight
Nginx session configuration:
# Session cache (server-side storage)
ssl_session_cache shared:SSL:50m;
ssl_session_timeout 1d;
# Session ticket key (for rotation)
ssl_session_ticket_key /etc/nginx/ssl_ticket.key;
# Generate new ticket key daily
ssl_session_ticket_key /etc/nginx/ssl_ticket_1.key;
ssl_session_ticket_key /etc/nginx/ssl_ticket_2.key;
Create session ticket keys:
# Generate ticket key
openssl rand 48 > /etc/nginx/ssl_ticket.key
# Rotate keys daily via cron
0 0 * * * openssl rand 48 > /etc/nginx/ssl_ticket_new.key && \
mv /etc/nginx/ssl_ticket.key /etc/nginx/ssl_ticket_1.key && \
mv /etc/nginx/ssl_ticket_new.key /etc/nginx/ssl_ticket.key && \
systemctl reload nginx
Apache session configuration:
# Session cache
SSLSessionCache shmcb:/var/run/apache2/ssl_scache(512000)
SSLSessionCacheTimeout 300
# Session ticket support (automatic in TLS 1.3)
# No explicit configuration needed
0-RTT (Early Data)
0-RTT allows clients to send data without waiting for server response, reducing latency.
Enable 0-RTT in Nginx:
server {
listen 443 ssl;
ssl_protocols TLSv1.3 TLSv1.2;
# Session tickets (required for 0-RTT)
ssl_session_cache shared:SSL:50m;
ssl_session_timeout 1d;
# 0-RTT is automatically enabled if session tickets are used
}
Test 0-RTT:
openssl s_client -tls1_3 -early_data <(echo "GET / HTTP/1.0") example.com:443
Risks of 0-RTT:
- Replay attack vulnerability (same request sent twice)
- Not suitable for non-idempotent operations (POST, PUT, DELETE)
- Applications must handle replay
Disable 0-RTT if necessary:
# Nginx doesn't expose 0-RTT control directly
# Disable session tickets to prevent 0-RTT
ssl_session_ticket off;
# Apache controls 0-RTT via OpenSSL
# Inherits from OpenSSL settings
Key Share Configuration
TLS 1.3 uses ephemeral key exchange curves. Configure supported curves:
Nginx elliptic curves:
server {
listen 443 ssl;
# Supported elliptic curves
ssl_ecdh_curve secp384r1:secp256r1:X25519:X448;
# Or more specific
ssl_ecdh_curve X25519:X448:secp384r1:secp256r1;
}
Recommended curves:
X25519 (modern, 128-bit symmetric strength)
X448 (alternative modern curve)
secp256r1 (P-256) (NIST, widely supported)
secp384r1 (P-384) (stronger, larger)
Nginx full optimization example:
server {
listen 443 ssl http2;
server_name example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
# TLS versions (TLS 1.3 and 1.2 only)
ssl_protocols TLSv1.3 TLSv1.2;
# Strong ciphers
ssl_ciphers TLS13-AES-256-GCM-SHA384:TLS13-CHACHA20-POLY1305-SHA256:TLS13-AES-128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers on;
# Key exchange curves
ssl_ecdh_curve X25519:X448:secp384r1:secp256r1;
# Session management
ssl_session_cache shared:SSL:50m;
ssl_session_timeout 1d;
ssl_session_ticket_key /etc/nginx/ssl_ticket.key;
# OCSP stapling
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
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 "DENY" always;
}
Performance Testing
Test TLS 1.3 performance and configuration:
Measure handshake performance:
# Single connection time
time openssl s_client -connect example.com:443 </dev/null
# Multiple connections
for i in {1..10}; do
time openssl s_client -connect example.com:443 </dev/null
done | grep real
Test with curl:
# Verbose TLS info
curl -v --tlsv1.3 https://example.com
# Show cipher used
curl -v https://example.com 2>&1 | grep cipher
# Benchmark multiple requests
for i in {1..10}; do
curl -w "Time: %{time_total}s\n" https://example.com
done
Use testssl.sh for comprehensive testing:
./testssl.sh --full https://example.com
# Check specific TLS version
./testssl.sh --tlsv1_3 https://example.com
Performance metrics to monitor:
Time To First Byte (TTFB)
Connection setup time
TLS handshake latency (reduced in TLS 1.3)
Session resumption speed (0-RTT)
Compatibility Considerations
Check client TLS 1.3 support:
# Clients supporting TLS 1.3
Chrome 51+
Firefox 52+
Safari 11+
Edge 79+
curl 7.56+
Python 3.6+ (with OpenSSL 1.1.1+)
Node.js 10.13.0+
Fallback configuration for older clients:
# Support TLS 1.2 as fallback
ssl_protocols TLSv1.3 TLSv1.2;
# Combined cipher list (TLS 1.3 + 1.2)
ssl_ciphers TLS13-AES-256-GCM-SHA384:TLS13-CHACHA20-POLY1305-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256;
Test backward compatibility:
# Test TLS 1.2
openssl s_client -tls1_2 -connect example.com:443
# Test TLS 1.3
openssl s_client -tls1_3 -connect example.com:443
# Test fallback
openssl s_client -tls1_2 -connect example.com:443
Conclusion
TLS 1.3 provides significant security and performance improvements over earlier versions. This guide covered TLS 1.3 fundamentals, cipher suite configuration, session resumption, 0-RTT handling, and optimization strategies for Nginx and Apache. For production deployments, enable TLS 1.3, maintain TLS 1.2 support for older clients, implement strong cipher suites, configure session resumption, and test with multiple clients. TLS 1.3 is now recommended for all modern deployments.


