Welcome to Part 5 of our comprehensive NGINX on Ubuntu series! We’ve set up static sites, virtual hosts, and PHP applications. Now it’s time to secure everything with SSL certificates using Let’s Encrypt and Certbot for free, automated HTTPS.
Understanding SSL/TLS and HTTPS
SSL/TLS certificates encrypt data between your server and clients, providing security, trust, and improved SEO rankings. Let’s Encrypt provides free certificates with automated renewal.
graph TD
A[Client Browser] --> B[NGINX Server :443]
B --> C[SSL Certificate Check]
C --> D{Certificate Valid?}
D -->|Yes| E[Establish TLS Connection]
D -->|No| F[Show Security Warning]
E --> G[Encrypted Communication]
G --> H[Serve Content via HTTPS]
I[Let's Encrypt CA] --> J[Issues Certificate]
J --> K[Certbot on Server]
K --> L[Auto-renewal every 60 days]
style B fill:#e1f5fe
style C fill:#e8f5e8
style G fill:#e8f5e8
style I fill:#fff3e0
Installing Certbot
Certbot is the official Let’s Encrypt client that automates certificate issuance and renewal.
# Update package index
sudo apt update
# Install Certbot and NGINX plugin
sudo apt install certbot python3-certbot-nginx -y
# Verify installation
certbot --versionPreparing Domain and DNS
Before obtaining certificates, ensure your domain points to your server’s public IP address.
# Check your server's public IP
curl -4 icanhazip.com
# Verify DNS resolution (replace with your domain)
nslookup example.com
dig example.com A
# Test HTTP access (should work before SSL)
curl -I http://example.comObtaining SSL Certificates
Method 1: Automatic NGINX Configuration
# Obtain certificate and auto-configure NGINX
sudo certbot --nginx -d example.com -d www.example.com
# For multiple domains
sudo certbot --nginx -d example.com -d www.example.com -d blog.example.com -d api.example.comMethod 2: Certificate Only (Manual Configuration)
# Obtain certificate without modifying NGINX config
sudo certbot certonly --nginx -d example.com -d www.example.com
# Alternative: Use webroot method
sudo certbot certonly --webroot -w /var/www/example.com/public_html -d example.com -d www.example.comManual NGINX SSL Configuration
If using certonly, configure NGINX manually for better control over SSL settings.
# Edit your virtual host
sudo nano /etc/nginx/sites-available/example.com# HTTP to HTTPS redirect
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
# Redirect all HTTP to HTTPS
return 301 https://$server_name$request_uri;
}
# HTTPS server block
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name example.com www.example.com;
root /var/www/example.com/public_html;
index index.html index.htm index.php;
# SSL Configuration
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
# SSL Security Settings
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_dhparam /etc/nginx/dhparam.pem;
# SSL Session Configuration
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
ssl_session_tickets off;
# OCSP Stapling
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
# 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;
# Main location block
location / {
try_files $uri $uri/ /index.php?$query_string;
}
# PHP processing (if needed)
location ~ \.php$ {
try_files $uri =404;
fastcgi_pass unix:/var/run/php/php8.3-fpm.sock;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
# Static file optimization
location ~* \.(css|js|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
expires 1y;
add_header Cache-Control "public, immutable";
access_log off;
}
# Security
location ~ /\. { deny all; }
# Logs
access_log /var/www/example.com/logs/access.log;
error_log /var/www/example.com/logs/error.log;
}Generating Strong Diffie-Hellman Parameters
# Generate strong DH parameters (takes several minutes)
sudo openssl dhparam -out /etc/nginx/dhparam.pem 2048
# Verify the file was created
ls -la /etc/nginx/dhparam.pemTesting SSL Configuration
# Test NGINX configuration
sudo nginx -t
# Reload NGINX
sudo systemctl reload nginx
# Test SSL certificate
openssl s_client -connect example.com:443 -servername example.com
# Check certificate details
echo | openssl s_client -servername example.com -connect example.com:443 2>/dev/null | openssl x509 -noout -dates
# Test HTTP to HTTPS redirect
curl -I http://example.comAutomatic Certificate Renewal
Let’s Encrypt certificates expire every 90 days. Certbot automatically sets up renewal.
graph TD
A[Certificate Issued] --> B[Valid for 90 days]
B --> C[Certbot checks daily]
C --> D{30 days left?}
D -->|No| E[Continue monitoring]
D -->|Yes| F[Attempt renewal]
F --> G{Renewal successful?}
G -->|Yes| H[Reload NGINX]
G -->|No| I[Log error, retry]
H --> J[Certificate renewed]
I --> F
E --> C
J --> B
style A fill:#e8f5e8
style F fill:#fff3e0
style H fill:#e1f5fe
style I fill:#ffebee
# Check renewal configuration
sudo certbot renew --dry-run
# View renewal timer
sudo systemctl status certbot.timer
sudo systemctl list-timers | grep certbot
# Manual renewal test
sudo certbot renew --force-renewal
# Check certificate status
sudo certbot certificatesConfiguring Multiple Domains
Separate Certificates for Each Domain
# Individual certificates
sudo certbot --nginx -d example.com -d www.example.com
sudo certbot --nginx -d blog.example.com
sudo certbot --nginx -d api.example.comWildcard Certificates
# Wildcard certificate (requires DNS validation)
sudo certbot certonly --manual --preferred-challenges dns -d "*.example.com" -d example.com
# Follow DNS TXT record instructions
# Add TXT record to your DNS: _acme-challenge.example.comSSL Security Best Practices
Create SSL Configuration Snippet
# Create reusable SSL configuration
sudo nano /etc/nginx/snippets/ssl-params.conf# SSL Security 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_dhparam /etc/nginx/dhparam.pem;
# Session Configuration
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-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;Use SSL Snippet in Virtual Hosts
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;
ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
# Include SSL security settings
include /etc/nginx/snippets/ssl-params.conf;
# Rest of configuration...
}Monitoring and Maintenance
# Check certificate expiry
sudo certbot certificates
# View renewal logs
sudo journalctl -u certbot
# Test SSL grade
curl -s "https://api.ssllabs.com/api/v3/analyze?host=example.com" | jq
# Monitor renewal attempts
sudo tail -f /var/log/letsencrypt/letsencrypt.log
# Force renewal for testing
sudo certbot renew --cert-name example.com --force-renewalTroubleshooting SSL Issues
Common Certificate Issues
# Certificate not found
sudo ls -la /etc/letsencrypt/live/
# Check NGINX error logs
sudo tail -f /var/log/nginx/error.log
# Test certificate chain
openssl verify -CAfile /etc/letsencrypt/live/example.com/chain.pem /etc/letsencrypt/live/example.com/cert.pem
# Debug SSL handshake
openssl s_client -connect example.com:443 -servername example.com -debugDNS Validation Issues
# Check DNS propagation
dig example.com A
nslookup example.com
# For wildcard certificates, check TXT records
dig _acme-challenge.example.com TXTWhat’s Next?
Excellent! You’ve successfully secured your NGINX server with SSL certificates from Let’s Encrypt. Your websites now serve traffic over HTTPS with automatic certificate renewal.
Coming up in Part 6: NGINX as a Reverse Proxy for Backend Applications
References
This is Part 5 of our 22-part NGINX series. Your websites are now secure with HTTPS! Next, we’ll explore reverse proxy configurations. Questions about SSL setup? Share them in the comments!
