🔒 Install SSL Let's Encrypt with Certbot + Nginx#
Secure your websites with free SSL certificates from Let's Encrypt using Certbot. This guide walks you through setting up HTTPS and configuring automatic renewal.
🎯 Objectives#
- 🔐 Install free Let's Encrypt SSL certificates
- ⚡ Configure Certbot for Nginx
- 🔄 Set up automatic renewal
- 🛡️ Optimize SSL configuration for security
- ✅ Test and validate HTTPS installation
🧰 Prerequisites#
- Nginx + PHP-FPM installed (see Nginx + PHP-FPM guide)
- Valid domain name pointing to your VPS
- Ports 80 and 443 open on your server
- Root or sudo access
💡 Important: This guide builds on the Nginx installation from the previous tutorial. Make sure you have Nginx properly configured first.
1️⃣ Preparation and verification#
🌐 Domain verification#
Before starting, verify that your domain points to your server:
# DNS verification nslookup your-domain.com dig your-domain.com # HTTP access test (must work) curl -I http://your-domain.com
🔧 Nginx verification#
# Nginx configuration verification sudo nginx -t # Service status sudo systemctl status nginx # Port testing sudo netstat -tlnp | grep -E ':80|:443'
2️⃣ Certbot installation#
📦 Installation via APT (Ubuntu/Debian)#
# Installation of Certbot and the Nginx plugin sudo apt update sudo apt install certbot python3-certbot-nginx -y
✅ Installation verification#
# Certbot test certbot --version # Available help and options certbot --help
3️⃣ SSL certificate obtaining#
🎫 First installation with automatic configuration#
Certbot can automatically modify your Nginx configuration:
# Automatic installation for a domain sudo certbot --nginx -d your-domain.com -d www.your-domain.com # Installation for multiple domains sudo certbot --nginx -d site1.com -d www.site1.com -d site2.com -d www.site2.com
During installation:
- Enter your email (expiration notifications)
- Accept the terms of use
- Choose whether you want to receive newsletters (optional)
- Certbot will automatically configure Nginx and create the HTTPS redirect
🔧 Manual installation (full control)#
If you prefer to control the configuration:
# Certificate acquisition only (no auto modification) sudo certbot certonly --nginx -d your-domain.com -d www.your-domain.com
4️⃣ Manual Nginx configuration (if manual installation)#
If you chose manual installation, modify your Nginx configuration:
📝 Complete SSL configuration#
# Modify your site sudo nano /etc/nginx/sites-available/mysite
Complete configuration with SSL:
# HTTP to HTTPS redirect server { listen 80; listen [::]:80; server_name your-domain.com www.your-domain.com; # Permanent redirect to HTTPS return 301 https://$server_name$request_uri; } # HTTPS configuration server { listen 443 ssl http2; listen [::]:443 ssl http2; server_name your-domain.com www.your-domain.com; # SSL certificates ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem; # Secure SSL configuration ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384; ssl_prefer_server_ciphers off; ssl_session_cache shared:SSL:10m; ssl_session_timeout 10m; # HTTPS security add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; add_header X-Frame-Options "SAMEORIGIN" always; add_header X-Content-Type-Options "nosniff" always; add_header X-XSS-Protection "1; mode=block" always; # Site configuration (identical to HTTP version) root /var/www/html/mysite; index index.php index.html index.htm; # Logs access_log /var/log/nginx/mysite_ssl_access.log; error_log /var/log/nginx/mysite_ssl_error.log; # PHP configuration location ~ \.php$ { include snippets/fastcgi-php.conf; fastcgi_pass unix:/run/php/php8.2-fpm.sock; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; } # Security location ~ /\.ht { deny all; } # Cache optimization location ~* \.(jpg|jpeg|png|gif|ico|css|js|pdf|zip)$ { expires 1y; add_header Cache-Control "public, immutable"; } # URL management location / { try_files $uri $uri/ /index.php?$query_string; } }
✅ Configuration application#
# Configuration test sudo nginx -t # Nginx reload sudo systemctl reload nginx
5️⃣ Optimized SSL configuration#
🔐 Creating a reusable SSL configuration file#
# Create an SSL configuration file sudo nano /etc/nginx/snippets/ssl-params.conf
Optimized SSL configuration:
# SSL protocols ssl_protocols TLSv1.2 TLSv1.3; # Secure ciphers ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES256-SHA; ssl_prefer_server_ciphers off; # SSL session cache ssl_session_cache shared:SSL:50m; ssl_session_timeout 1d; 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-Frame-Options "SAMEORIGIN" always; add_header X-Content-Type-Options "nosniff" always; add_header X-XSS-Protection "1; mode=block" always; add_header Referrer-Policy "no-referrer-when-downgrade" always;
🔄 Using the SSL file in your sites#
Modify your site configurations to use this file:
server { listen 443 ssl http2; server_name your-domain.com; # Certificates ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem; # Include SSL configuration include /etc/nginx/snippets/ssl-params.conf; # ... rest of configuration }
6️⃣ Automatic renewal#
🔄 Renewal configuration#
Let's Encrypt generates certificates valid for 90 days. Automatic renewal is essential:
# Renewal test (dry-run) sudo certbot renew --dry-run # Verify installed certificates sudo certbot certificates
⏰ Cron configuration for renewal#
# Edit root crontab sudo crontab -e
Add the following line (official recommendation):
# Let's Encrypt automatic renewal (twice daily) 0 */12 * * * root certbot -q renew --nginx
7️⃣ Tests and validation#
🧪 Basic tests#
# Local HTTPS test curl -I https://your-domain.com # Certificate verification openssl s_client -connect your-domain.com:443 -servername your-domain.com
🌐 Online tests#
- SSL Labs: https://www.ssllabs.com/ssltest/
- Security Headers: https://securityheaders.com/
- Certificate Transparency: https://crt.sh/
📊 Score verification#
A good setup should achieve:
- Grade A or A+ on SSL Labs
- Grade A on Security Headers
- Green on all compatibility tests
8️⃣ Multiple domain management#
🌍 Adding a new domain#
# Add a certificate for a new site sudo certbot --nginx -d new-site.com -d www.new-site.com # Or extend an existing certificate sudo certbot --nginx --expand -d old-site.com -d www.old-site.com -d new-site.com
🔧 Subdomain management#
# Wildcard certificate (requires DNS validation) sudo certbot certonly --manual --preferred-challenges dns -d "*.your-domain.com" -d your-domain.com
9️⃣ Troubleshooting#
🔍 Common problems#
Error: "Challenge failed"
# Verify domain accessibility curl -I http://your-domain.com/.well-known/acme-challenge/test # Verify Nginx configuration sudo nginx -t
Error: "Certificate already exists"
# Force renewal sudo certbot renew --force-renewal # Or delete and reinstall sudo certbot delete --cert-name your-domain.com sudo certbot --nginx -d your-domain.com
Error: "Port 80 not accessible"
# Firewall verification sudo ufw status sudo iptables -L # Port verification sudo netstat -tlnp | grep :80
📋 Useful logs#
# Certbot logs sudo tail -f /var/log/letsencrypt/letsencrypt.log # Nginx logs sudo tail -f /var/log/nginx/error.log # Detailed configuration test sudo certbot renew --dry-run --verbose
✅ Installation summary#
Your Let's Encrypt SSL installation is now complete with:
- 🔒 SSL certificates free and valid
- ⚡ HTTPS configuration optimized
- 🔄 Automatic renewal configured
- 🛡️ Enhanced security with appropriate headers