
How to Secure Nginx with Let’s Encrypt on Ubuntu 24
Securing Nginx with Let’s Encrypt on Ubuntu 24 is essential for anyone running web services who wants to implement SSL/TLS encryption without the cost and complexity of traditional certificate authorities. Let’s Encrypt provides free, automated certificates that are trusted by all major browsers, making HTTPS accessible to everyone. In this guide, you’ll learn how to install and configure Let’s Encrypt with Nginx on Ubuntu 24, automate certificate renewal, handle multiple domains, and troubleshoot common issues that can trip up even experienced administrators.
How Let’s Encrypt and Certbot Work
Let’s Encrypt operates on the ACME (Automatic Certificate Management Environment) protocol, which automates the traditionally manual process of certificate creation, validation, signing, and installation. When you request a certificate, Let’s Encrypt needs to verify that you control the domain you’re requesting it for.
The validation happens through challenges – typically either HTTP-01 (placing a file at a specific URL on your web server) or DNS-01 (creating a specific DNS TXT record). Certbot, the official Let’s Encrypt client, handles these challenges automatically and can even modify your Nginx configuration to implement the SSL settings.
Certificates from Let’s Encrypt are valid for 90 days, which encourages automation. This shorter lifespan actually improves security by limiting the window of exposure if a private key gets compromised, and forces you to set up proper renewal processes from the start.
Prerequisites and Initial Setup
Before diving into the SSL setup, make sure your Ubuntu 24 system is properly configured. You’ll need a domain name pointing to your server’s IP address and Nginx already installed and serving content.
sudo apt update
sudo apt install nginx
sudo systemctl start nginx
sudo systemctl enable nginx
Verify that Nginx is running and accessible:
sudo systemctl status nginx
curl -I http://your-domain.com
Install snapd if it’s not already present, as the recommended way to install Certbot is through snap packages:
sudo apt install snapd
sudo snap install core; sudo snap refresh core
sudo snap install --classic certbot
Create a symbolic link to make Certbot accessible system-wide:
sudo ln -s /snap/bin/certbot /usr/bin/certbot
Configuring Nginx for Let’s Encrypt
Before obtaining certificates, you need a basic Nginx configuration. Create a server block for your domain:
sudo nano /etc/nginx/sites-available/your-domain.com
Add this basic configuration:
server {
listen 80;
server_name your-domain.com www.your-domain.com;
root /var/www/your-domain.com;
index index.html index.htm index.nginx-debian.html;
location / {
try_files $uri $uri/ =404;
}
# Let's Encrypt validation
location /.well-known/acme-challenge/ {
root /var/www/your-domain.com;
}
}
Enable the site and test the configuration:
sudo ln -s /etc/nginx/sites-available/your-domain.com /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
Create the web root directory and add a simple index file for testing:
sudo mkdir -p /var/www/your-domain.com
echo "Hello from your-domain.com
" | sudo tee /var/www/your-domain.com/index.html
Obtaining SSL Certificates
Now comes the main event – getting your SSL certificate. Certbot offers several methods, but the Nginx plugin is the most straightforward as it automatically modifies your configuration:
sudo certbot --nginx -d your-domain.com -d www.your-domain.com
During the process, Certbot will ask for your email address and agreement to terms of service. Choose whether to redirect HTTP traffic to HTTPS (recommended for most cases).
If you prefer more control over the process, you can obtain the certificate without automatic configuration changes:
sudo certbot certonly --nginx -d your-domain.com -d www.your-domain.com
For multiple unrelated domains, you can obtain separate certificates:
sudo certbot --nginx -d domain1.com -d www.domain1.com
sudo certbot --nginx -d domain2.com -d www.domain2.com
After successful certificate generation, Certbot will show you the certificate and key locations, typically:
- Certificate: /etc/letsencrypt/live/your-domain.com/fullchain.pem
- Private Key: /etc/letsencrypt/live/your-domain.com/privkey.pem
Optimizing SSL Configuration
Certbot’s default SSL configuration is decent, but you can enhance security and performance. Here’s an optimized configuration that incorporates modern security practices:
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name your-domain.com www.your-domain.com;
# SSL Configuration
ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem;
# SSL Security Settings
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384;
ssl_prefer_server_ciphers off;
# SSL Performance
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
ssl_session_tickets off;
# OCSP Stapling
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/letsencrypt/live/your-domain.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";
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
root /var/www/your-domain.com;
index index.html index.htm index.nginx-debian.html;
location / {
try_files $uri $uri/ =404;
}
}
server {
listen 80;
listen [::]:80;
server_name your-domain.com www.your-domain.com;
# Redirect all HTTP requests to HTTPS
return 301 https://$server_name$request_uri;
}
Setting Up Automatic Renewal
Let’s Encrypt certificates expire every 90 days, so automatic renewal is crucial. Certbot installs a systemd timer for this purpose. Check if it’s active:
sudo systemctl status snap.certbot.renew.timer
Test the renewal process without actually renewing:
sudo certbot renew --dry-run
You can also set up a custom cron job for more control over the renewal process:
sudo crontab -e
Add this line to run renewal twice daily:
0 12,23 * * * /usr/bin/certbot renew --quiet --post-hook "systemctl reload nginx"
For monitoring renewal status, create a simple script that logs renewal attempts:
#!/bin/bash
LOG_FILE="/var/log/certbot-renewal.log"
echo "$(date): Starting certificate renewal check" >> $LOG_FILE
/usr/bin/certbot renew --quiet --post-hook "systemctl reload nginx" >> $LOG_FILE 2>&1
echo "$(date): Certificate renewal check completed" >> $LOG_FILE
Handling Multiple Domains and Wildcard Certificates
For wildcard certificates, you’ll need to use DNS validation since HTTP validation can’t verify all possible subdomains:
sudo certbot certonly --manual --preferred-challenges dns -d your-domain.com -d *.your-domain.com
This process requires you to add TXT records to your DNS configuration. For automation, you can use DNS plugins for popular providers:
sudo snap install certbot-dns-cloudflare
sudo certbot certonly --dns-cloudflare --dns-cloudflare-credentials ~/.secrets/cloudflare.ini -d your-domain.com -d *.your-domain.com
The Cloudflare credentials file should contain:
# Cloudflare API credentials
dns_cloudflare_api_token = your-api-token
Set proper permissions on the credentials file:
chmod 600 ~/.secrets/cloudflare.ini
Performance Comparison and SSL Testing
Here’s a comparison of SSL configuration performance impacts:
Configuration | Handshake Time (ms) | CPU Usage | Security Score |
---|---|---|---|
Basic Certbot | 45-60 | Low | A |
Optimized TLS 1.2/1.3 | 25-35 | Medium | A+ |
With OCSP Stapling | 20-30 | Medium | A+ |
HTTP/2 Enabled | 20-30 | Medium | A+ |
Test your SSL configuration using online tools:
You can also test locally using OpenSSL:
openssl s_client -connect your-domain.com:443 -servername your-domain.com
echo | openssl s_client -connect your-domain.com:443 2>/dev/null | openssl x509 -noout -dates
Common Issues and Troubleshooting
Rate limiting is a frequent issue when testing. Let’s Encrypt has several rate limits:
- 20 certificates per registered domain per week
- 300 new orders per account per 3 hours
- 5 duplicate certificates per week
- 10 accounts per IP address per 3 hours
If you hit rate limits, use the staging environment for testing:
sudo certbot --nginx --staging -d your-domain.com
Port 80 must be accessible for HTTP-01 challenges. Check firewall settings:
sudo ufw status
sudo ufw allow 'Nginx Full'
For DNS resolution issues, verify your domain points to the correct IP:
dig your-domain.com
nslookup your-domain.com
If Certbot fails to modify Nginx configuration, check for syntax errors:
sudo nginx -t
sudo tail -f /var/log/nginx/error.log
Certificate chain issues can cause browser warnings. Verify the full chain:
openssl verify -CAfile /etc/ssl/certs/ca-certificates.crt /etc/letsencrypt/live/your-domain.com/fullchain.pem
Real-World Use Cases and Advanced Configurations
For high-traffic sites, consider implementing SSL session resumption and HTTP/2 push:
server {
listen 443 ssl http2;
# SSL session resumption for better performance
ssl_session_cache shared:SSL:50m;
ssl_session_timeout 1d;
ssl_session_tickets off;
# HTTP/2 Server Push for critical resources
location = /index.html {
add_header Link "; rel=preload; as=style, ; rel=preload; as=script";
try_files $uri $uri/ =404;
}
}
For API servers, you might want to implement certificate pinning headers:
add_header Public-Key-Pins 'pin-sha256="primary-key-hash"; pin-sha256="backup-key-hash"; max-age=2592000; includeSubDomains';
Load balancer configurations require special consideration for certificate management:
upstream backend {
server 10.0.1.10:80;
server 10.0.1.11:80;
}
server {
listen 443 ssl http2;
server_name api.your-domain.com;
ssl_certificate /etc/letsencrypt/live/api.your-domain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/api.your-domain.com/privkey.pem;
location / {
proxy_pass http://backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Best Practices and Security Considerations
Always backup your certificates and keys, especially before major system updates:
sudo cp -r /etc/letsencrypt /backup/letsencrypt-$(date +%Y%m%d)
Monitor certificate expiration dates proactively:
#!/bin/bash
for cert in /etc/letsencrypt/live/*/cert.pem; do
domain=$(basename $(dirname $cert))
expiry=$(openssl x509 -enddate -noout -in $cert | cut -d= -f2)
echo "Domain: $domain expires on $expiry"
done
Implement proper log monitoring for SSL-related errors:
sudo tail -f /var/log/nginx/error.log | grep -i ssl
Consider using configuration management tools like Ansible for consistent SSL deployments across multiple servers. For high-availability setups, you might want to explore dedicated server solutions that provide the resources needed for complex SSL termination scenarios.
Regular security audits should include checking for weak cipher suites, outdated protocols, and proper HTTP security headers. The configuration provided here follows current best practices, but security standards evolve, so stay updated with the latest recommendations from Mozilla’s SSL Configuration Generator.
For development environments or smaller projects, VPS hosting can provide an excellent platform for implementing these SSL configurations while maintaining cost efficiency and full control over your server environment.

This article incorporates information and material from various online sources. We acknowledge and appreciate the work of all original authors, publishers, and websites. While every effort has been made to appropriately credit the source material, any unintentional oversight or omission does not constitute a copyright infringement. All trademarks, logos, and images mentioned are the property of their respective owners. If you believe that any content used in this article infringes upon your copyright, please contact us immediately for review and prompt action.
This article is intended for informational and educational purposes only and does not infringe on the rights of the copyright owners. If any copyrighted material has been used without proper credit or in violation of copyright laws, it is unintentional and we will rectify it promptly upon notification. Please note that the republishing, redistribution, or reproduction of part or all of the contents in any form is prohibited without express written permission from the author and website owner. For permissions or further inquiries, please contact us.