How to Configure Production-ready Ssl/tls in Nginx with Let’s Encrypt and Tls 1.3 Hardening
How to configure production-ready SSL/TLS in Nginx with Let’s Encrypt and TLS 1.3 hardening is essential for securing modern web applications. This comprehensive tutorial will guide you through implementing enterprise-grade SSL/TLS configuration that meets current security standards. You’ll learn to deploy free SSL certificates from Let’s Encrypt, configure TLS 1.3 for optimal performance, and implement security hardening measures that protect against common vulnerabilities.
Modern web security demands more than basic SSL implementation. Search engines prioritize HTTPS sites, and users expect secure connections. This tutorial covers automatic certificate renewal, perfect forward secrecy, HSTS headers, and cipher suite optimization. By the end, you’ll have a production-ready Nginx server with A+ SSL ratings and enterprise-level security.
The configuration we’ll build includes TLS 1.3 support, OCSP stapling, and security headers that protect against XSS, clickjacking, and other attacks. These implementations are crucial for compliance with security frameworks and maintaining user trust in production environments.
Prerequisites and SSL/TLS Configuration Requirements
Before starting this SSL/TLS configuration tutorial, ensure you have the following requirements in place. You’ll need a Ubuntu 20.04 or 22.04 server with root access and at least 1GB of RAM. Your domain must be pointed to your server’s IP address through DNS A records.
Install Nginx if it’s not already present on your system:
sudo apt update
sudo apt install nginx -y
sudo systemctl enable nginx
sudo systemctl start nginx
You’ll also need Certbot for Let’s Encrypt certificate management. Install the official Certbot package and Nginx plugin:
sudo apt install certbot python3-certbot-nginx -y
Verify your domain resolves correctly by testing DNS resolution. Replace example.com with your actual domain:
nslookup example.com
This tutorial assumes basic Linux command line knowledge and familiarity with text editors like nano or vim. The entire process takes approximately 30-45 minutes to complete. Ensure port 80 and 443 are open in your firewall settings, as Let’s Encrypt requires these ports for domain validation.
Step-by-Step Guide to Configure Production-ready SSL/TLS in Nginx
For more strange history, see: How to Configure Ssh Key-based Authentication on Ubuntu Server
Step 1: Create initial Nginx server block configuration for your domain. This basic configuration allows Let’s Encrypt to validate domain ownership:
sudo nano /etc/nginx/sites-available/example.com
Add the following basic server block configuration:
server {
listen 80;
server_name example.com www.example.com;
location / {
root /var/www/html;
index index.html;
}
location ~ /.well-known/acme-challenge {
allow all;
root /var/www/html;
}
}
Step 2: Enable the site and test the configuration. Create a symbolic link and verify Nginx syntax:
sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
Step 3: Obtain SSL certificates from Let’s Encrypt using Certbot. The Nginx plugin automatically configures HTTPS:
sudo certbot --nginx -d example.com -d www.example.com
Certbot will prompt for email address and agreement to terms. Choose option 2 to redirect all HTTP traffic to HTTPS when prompted. This command automatically modifies your Nginx configuration to include SSL settings.
Step 4: Implement TLS 1.3 hardening and security optimizations. Edit your Nginx configuration to add advanced SSL settings:
sudo nano /etc/nginx/sites-available/example.com
Replace the SSL configuration section with these hardened settings:
server {
listen 443 ssl http2;
server_name example.com www.example.com;
# SSL Certificate Configuration
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
# TLS 1.3 and Protocol Configuration
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off;
# Modern Cipher Suites
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;
# 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=31536000; includeSubDomains; preload" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:;" always;
# Session Configuration
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
ssl_session_tickets off;
location / {
root /var/www/html;
index index.html;
}
}
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$server_name$request_uri;
}
Step 5: Configure Diffie-Hellman parameters for perfect forward secrecy. Generate strong DH parameters:
sudo openssl dhparam -out /etc/nginx/dhparam.pem 2048
Add the DH parameter configuration to your server block:
ssl_dhparam /etc/nginx/dhparam.pem;
Step 6: Test and reload the configuration. Verify syntax and apply changes:
sudo nginx -t
sudo systemctl reload nginx
Step 7: Set up automatic certificate renewal. Let’s Encrypt certificates expire every 90 days, so automation is crucial:
sudo crontab -e
Add this line to run renewal checks twice daily:
0 12,0 /usr/bin/certbot renew --quiet && /usr/bin/systemctl reload nginx
Step 8: Verify SSL configuration using online tools. Test your implementation at SSL Labs SSL Test to ensure you achieve an A+ rating.
Troubleshooting Common SSL/TLS Configuration Issues
When implementing production-ready SSL/TLS in Nginx configurations, several common issues may occur. Certificate validation failures often result from incorrect DNS settings or firewall blocking. Verify your domain points to the correct IP address and ensure ports 80 and 443 are accessible.
If Certbot fails with “too many requests” errors, you’ve hit Let’s Encrypt rate limits. Wait one hour before retrying, or use the staging environment for testing:
sudo certbot --nginx --staging -d example.com
Mixed content warnings appear when HTTPS pages load HTTP resources. Update all internal links, images, and scripts to use HTTPS or relative URLs. The Content Security Policy header helps identify these issues in browser developer tools.
Certificate renewal failures typically occur due to changed server configurations. Test renewal manually to identify issues:
sudo certbot renew --dry-run
If you encounter “SSL_ERROR_RX_RECORD_TOO_LONG” errors, check that your server block doesn’t have conflicting listen directives. Ensure only one server block handles SSL on port 443 for each domain.
For performance issues, monitor SSL handshake times and consider adjusting session cache settings. The official Nginx HTTPS documentation provides detailed optimization guidance.
Browser compatibility issues with older clients may require adjusting cipher suites. However, prioritize security over legacy support in production environments. Modern security standards should take precedence over outdated browser compatibility.
Advanced Security Optimization and Monitoring
Beyond basic SSL/TLS implementation, production environments require ongoing security monitoring and optimization. Implement log monitoring to detect unusual SSL handshake patterns or certificate-related errors. Configure Nginx access logs to capture SSL protocol versions and cipher suites:
log_format ssl_combined '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$
