Securing Nginx with Let’s Encrypt on Debian provides free, automated SSL/TLS certificates that encrypt traffic between your server and visitors. Whether you’re hosting a WordPress site, running a web application, or serving static content, HTTPS encryption protects sensitive data and improves search engine rankings. As a result, by the end of this guide, you’ll have a fully configured SSL certificate with automatic renewal, HTTP-to-HTTPS redirection, and optimized security settings.
Prerequisites
Before generating SSL certificates, ensure you have the following in place:
- A Debian server with root or sudo access
- Nginx installed and running (see how to install Nginx on Debian if needed)
- A registered domain name with DNS A/AAAA records pointing to your server’s IP address
- An existing Nginx server block configured for your domain in
/etc/nginx/sites-available/ - Ports 80 (HTTP) and 443 (HTTPS) open in your firewall (see how to configure UFW on Debian for firewall setup)
Let’s Encrypt uses HTTP-01 validation to verify domain ownership. Certbot places a temporary file on your server, then Let’s Encrypt requests it over HTTP (port 80). This requires your domain’s DNS A record to point to your server and port 80 to be accessible from the internet.
Update System Packages
Before installing Certbot, update your package repositories and upgrade existing packages to ensure you install the latest versions with all security patches:
sudo apt update && sudo apt upgrade
Confirm any prompts to proceed with the upgrade. Once complete, your system is ready for Certbot installation.
Install Certbot and Nginx Plugin
Certbot is the official Let’s Encrypt client that handles certificate generation, installation, and renewal. Additionally, the Nginx plugin enables Certbot to automatically configure your web server and manage certificates without manual intervention.
First, install both packages:
sudo apt install certbot python3-certbot-nginx
Next, verify the installation by checking the Certbot version:
certbot --version
certbot 4.0.0
Note that the version number may vary depending on your Debian release. Debian 11 includes Certbot 1.12, Debian 12 includes 2.1, and Debian 13 includes 4.0. All versions support the commands in this guide.
Generate SSL Certificate with Certbot
With Certbot installed, you can generate an SSL certificate for your domain. The following command uses recommended security options that enable HTTPS redirection, HSTS (HTTP Strict Transport Security) headers, and OCSP (Online Certificate Status Protocol) stapling in a single step.
Replace you@example.com with your email address and yourdomain.com with your actual domain name:
sudo certbot --nginx --agree-tos --redirect --hsts --staple-ocsp --email you@example.com -d yourdomain.com
To include both the apex domain and www subdomain in a single certificate, add multiple -d flags:
sudo certbot --nginx --agree-tos --redirect --hsts --staple-ocsp --email you@example.com -d yourdomain.com -d www.yourdomain.com
The command options configure the following:
| Option | Purpose |
|---|---|
--nginx | Uses the Nginx plugin to automatically configure your server block |
--agree-tos | Accepts the Let’s Encrypt terms of service |
--redirect | Sets up a permanent 301 redirect from HTTP to HTTPS |
--hsts | Adds Strict-Transport-Security header to enforce HTTPS |
--staple-ocsp | Enables OCSP stapling for faster SSL verification |
--email | Email address for renewal reminders and security notices |
-d | Domain name(s) to include in the certificate |
Upon successful completion, Certbot displays the certificate location and expiration date:
Successfully received certificate. Certificate is saved at: /etc/letsencrypt/live/yourdomain.com/fullchain.pem Key is saved at: /etc/letsencrypt/live/yourdomain.com/privkey.pem This certificate expires on 2026-03-02. These files will be updated when the certificate renews.
Verify the Certificate
List all certificates managed by Certbot to confirm successful installation:
sudo certbot certificates
Found the following certs:
Certificate Name: yourdomain.com
Domains: yourdomain.com
Expiry Date: 2026-03-02 (VALID: 89 days)
Certificate Path: /etc/letsencrypt/live/yourdomain.com/fullchain.pem
Private Key Path: /etc/letsencrypt/live/yourdomain.com/privkey.pem
Alternatively, you can verify the certificate by visiting your domain in a browser. Look for the padlock icon in the address bar indicating a secure HTTPS connection.
Interactive Certificate Generation
If you prefer a guided approach instead, Certbot provides an interactive mode that prompts for each configuration option. This method is particularly helpful when securing multiple domains or when you want to review each setting before applying it.
sudo certbot --nginx
Certbot will prompt you through the following steps:
- Email address: Enter your email for renewal reminders and security notices
- Terms of service: Input
Ato agree - EFF newsletter: Input
YorNfor Electronic Frontier Foundation updates - Domain selection: Enter the numbers for domains to secure, or leave blank for all detected domains
- HTTPS redirect: Select
2to redirect all HTTP traffic to HTTPS (recommended)
Finally, after completing the prompts, Certbot generates the certificate and updates your Nginx configuration automatically.
Test Certificate Renewal
Let’s Encrypt certificates expire after 90 days, so automatic renewal is essential. Therefore, before relying on the automatic process, verify that renewal works correctly with a dry run:
sudo certbot renew --dry-run
A successful test displays:
Saving debug log to /var/log/letsencrypt/letsencrypt.log - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Processing /etc/letsencrypt/renewal/yourdomain.com.conf - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Simulating renewal of an existing certificate for yourdomain.com - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Congratulations, all simulated renewals succeeded: /etc/letsencrypt/live/yourdomain.com/fullchain.pem (success)
Conversely, if the dry run fails, check that port 80 is accessible and your DNS records point to the correct server IP.
Enable Automatic Certificate Renewal
The Certbot package on Debian includes a systemd timer that automatically attempts certificate renewal twice daily. Consequently, this timer is the recommended method for automatic renewal, so no manual cron configuration is needed.
To begin, enable and start the timer:
sudo systemctl enable --now certbot.timer
Then, verify the timer is active:
sudo systemctl status certbot.timer
● certbot.timer - Run certbot twice daily
Loaded: loaded (/lib/systemd/system/certbot.timer; enabled; preset: enabled)
Active: active (waiting) since Mon 2025-12-02 10:00:00 UTC; 1min ago
Trigger: Mon 2025-12-02 12:00:00 UTC; 1h 59min left
Triggers: ● certbot.service
The timer runs at midnight and noon each day with a randomized delay to distribute load across Let’s Encrypt servers. Furthermore, certificates are only renewed when they are within 30 days of expiration.
To view the timer schedule:
systemctl list-timers certbot.timer
NEXT LEFT LAST PASSED UNIT ACTIVATES Mon 2025-12-02 12:00:00 UTC 1h 30min left Mon 2025-12-02 00:00:00 UTC 10h ago certbot.timer certbot.service
Optimize SSL Configuration (Optional)
Certbot’s automatic configuration provides a secure baseline, but you can further optimize SSL settings for improved security and performance. Specifically, this section covers advanced hardening based on Mozilla’s SSL Configuration Generator recommendations.
These optimizations are optional. Certbot’s default configuration is secure for most use cases. Only proceed if you understand the implications of each setting.
Edit the Nginx Configuration File
Open your domain’s Nginx server block configuration:
sudo nano /etc/nginx/sites-available/yourdomain.com
Certbot has already added SSL directives. You can enhance these settings within the server block that listens on port 443.
Configure SSL Certificate Paths
Certbot automatically sets these paths, but for reference, Let’s Encrypt certificates are stored at:
ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
Configure SSL Session Parameters
SSL session caching reduces the overhead of SSL handshakes for returning visitors:
ssl_session_timeout 1d;
ssl_session_cache shared:MozSSL:10m;
ssl_session_tickets off;
The shared cache stores approximately 40,000 sessions in 10MB of memory. Moreover, disabling session tickets improves forward secrecy.
Generate Diffie-Hellman Parameters (TLS 1.2 Only)
Diffie-Hellman parameters strengthen key exchange for TLS 1.2 connections. However, TLS 1.3 uses ECDHE exclusively and does not require DH parameters, so this step is only necessary if you support TLS 1.2 clients.
Generating DH parameters can take several minutes depending on your server’s CPU. A 2048-bit key is sufficient for most use cases; 4096-bit provides additional security at the cost of longer generation time and slightly slower connections.
Generate a 2048-bit DH parameter file:
sudo openssl dhparam -out /etc/ssl/certs/dhparam.pem 2048
Alternatively, for environments requiring stronger security, generate a 4096-bit key instead:
sudo openssl dhparam -out /etc/ssl/certs/dhparam.pem 4096
Afterward, add the DH parameter path to your Nginx configuration:
ssl_dhparam /etc/ssl/certs/dhparam.pem;
Configure TLS Protocols and Cipher Suites
Modern browsers support TLS 1.2 and 1.3. The following configuration uses Mozilla’s “Intermediate” cipher suite, which balances security with broad compatibility:
ssl_protocols TLSv1.2 TLSv1.3;
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:DHE-RSA-CHACHA20-POLY1305;
ssl_prefer_server_ciphers off;
In particular, setting ssl_prefer_server_ciphers off allows modern clients to negotiate the most efficient cipher, typically resulting in better performance.
Enable HSTS Header
HTTP Strict Transport Security (HSTS) instructs browsers to only connect via HTTPS, preventing protocol downgrade attacks:
add_header Strict-Transport-Security "max-age=63072000" always;
The max-age value of 63072000 seconds equals two years. As a result, once set, browsers will refuse HTTP connections to your domain for that duration. For additional protection, see how to configure security headers in Nginx to add headers like X-Frame-Options and Content-Security-Policy.
Configure OCSP Stapling
OCSP stapling allows your server to provide certificate revocation status directly, improving connection speed and visitor privacy:
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/letsencrypt/live/yourdomain.com/chain.pem;
The ssl_trusted_certificate directive points to the Let’s Encrypt intermediate certificate chain, which Nginx uses to verify OCSP responses.
Set DNS Resolver for OCSP
Nginx requires a DNS resolver to fetch OCSP responses. Configure a reliable public DNS server:
resolver 1.1.1.1 1.0.0.1 valid=300s;
resolver_timeout 5s;
This configuration uses Cloudflare’s DNS servers. Of course, you can substitute Google DNS (8.8.8.8 8.8.4.4) or your ISP’s resolvers if preferred.
Test and Apply Configuration
After making changes, validate the Nginx configuration syntax:
sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful
If the test passes, reload Nginx to apply the changes:
sudo systemctl reload nginx
Notably, using reload instead of restart applies configuration changes without dropping active connections.
Troubleshoot Common Issues
If certificate generation or renewal fails, use these diagnostic steps to identify the problem.
Domain Validation Fails
If Certbot reports a validation error, the HTTP-01 challenge cannot reach your server:
Challenge failed for domain yourdomain.com IMPORTANT NOTES: - The following errors were reported by the server: Domain: yourdomain.com Type: unauthorized Detail: Invalid response from http://yourdomain.com/.well-known/acme-challenge/...
First, check that your DNS A record points to your server:
getent hosts yourdomain.com
203.0.113.10 yourdomain.com
Next, verify port 80 is open and Nginx is running:
sudo ss -tlnp | grep ':80'
LISTEN 0 511 0.0.0.0:80 0.0.0.0:* users:(("nginx",pid=1234,fd=6))
Similarly, if using UFW, ensure HTTP traffic is allowed:
sudo ufw status | grep -E '80|Nginx'
80/tcp ALLOW Anywhere Nginx Full ALLOW Anywhere
If you see no output, HTTP traffic is blocked. Enable it with sudo ufw allow 80/tcp or sudo ufw allow 'Nginx Full'.
After correcting DNS or firewall issues, retry the Certbot command to generate your certificate.
Nginx Configuration Errors
If nginx -t reports syntax errors after Certbot runs:
nginx: [emerg] unknown directive "ssl_certificate" in /etc/nginx/sites-enabled/yourdomain.com:15
This typically means the SSL module is not loaded. To confirm, check your Nginx installation:
nginx -V 2>&1 | grep -o 'with-http_ssl_module'
If the module is missing, reinstall Nginx with full modules:
sudo apt install --reinstall nginx-full
Verify the SSL module is now available:
nginx -V 2>&1 | grep -o 'with-http_ssl_module'
with-http_ssl_module
With the module present, retry the Certbot command.
Rate Limit Errors
Let’s Encrypt enforces rate limits to prevent abuse. If you see:
Error creating new order :: too many certificates already issued for exact set of domains
In this case, wait one week before requesting another certificate for the same domain, or use the staging environment for testing:
sudo certbot --nginx --staging -d yourdomain.com
Importantly, staging certificates are not trusted by browsers but do not count against rate limits.
Remove Certbot and Certificates
If you need to remove Let’s Encrypt certificates and Certbot from your system, follow these steps.
Revoke and Delete Certificates
Revoking a certificate notifies browsers that it should no longer be trusted. Only revoke if the private key was compromised or you’re permanently decommissioning the domain.
To revoke and then delete a certificate:
sudo certbot revoke --cert-name yourdomain.com
sudo certbot delete --cert-name yourdomain.com
Alternatively, if you only want to remove the certificate without revoking:
sudo certbot delete --cert-name yourdomain.com
Remove Certbot Packages
Disable the renewal timer and uninstall Certbot:
sudo systemctl disable --now certbot.timer
sudo apt remove --purge certbot python3-certbot-nginx
sudo apt autoremove
Clean Up Configuration
The following commands permanently delete all Let’s Encrypt certificates, renewal configurations, and logs. If you have other domains using Certbot certificates on this server, do not run these commands. Instead, remove only the specific certificate with
certbot delete --cert-name.
Remove Let’s Encrypt directories and any remaining configuration:
sudo rm -rf /etc/letsencrypt
sudo rm -rf /var/lib/letsencrypt
sudo rm -rf /var/log/letsencrypt
Edit your Nginx server block to remove SSL directives added by Certbot, then test and reload:
sudo nginx -t && sudo systemctl reload nginx
Verify Certbot is fully removed:
apt-cache policy certbot
certbot:
Installed: (none)
Candidate: 2.1.0-4
Version table:
2.1.0-4 500
500 http://deb.debian.org/debian bookworm/main amd64 Packages
The Installed: (none) line confirms successful removal.
Conclusion
You now have Nginx secured with a free Let’s Encrypt SSL certificate on Debian. The configuration includes automatic renewal via systemd timer, HTTP-to-HTTPS redirection, and optional advanced SSL hardening with HSTS and OCSP stapling. For additional security, consider setting up Fail2ban on Debian to protect against brute-force attacks, or explore deploying WordPress with Nginx on Debian to put your secure server to use.