How to Secure Nginx with Let’s Encrypt on Debian (13, 12, 11)

Last updated Friday, February 27, 2026 1:35 pm 12 min read

Every site served over plain HTTP leaks login credentials, session cookies, and form data to anyone listening on the wire. The fastest way to secure Nginx with Let’s Encrypt on Debian is Certbot, which generates a free SSL/TLS certificate, rewrites your server block for HTTPS, and sets up automatic renewal so nothing expires silently. You get a working HTTPS redirect, HSTS headers, and OCSP stapling in a single command, with optional cipher hardening on top.

Prerequisites for Certbot and Nginx on Debian

Certbot needs a few things already working before it can issue a certificate. These prerequisites apply to Debian 13 (Trixie), Debian 12 (Bookworm), and Debian 11 (Bullseye):

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 Debian System Packages

Update your package index and apply any pending upgrades before adding new packages:

sudo apt update && sudo apt upgrade

If your account does not have sudo privileges, see how to add a user to sudoers on Debian before continuing.

Install Certbot and the Nginx Plugin on Debian

Certbot is the official Let’s Encrypt client that handles certificate generation, installation, and renewal. The Nginx plugin lets Certbot automatically configure your web server and manage certificates without manual edits.

Install both packages from the default Debian repositories. On Debian 12, this pulls certbot 2.1 and the python3-certbot-nginx plugin; Debian 13 provides Certbot 4.0 with the same package names:

sudo apt install certbot python3-certbot-nginx

Verify the installation:

certbot --version
certbot 4.0.0

The version number varies by Debian release. Debian 12 (Bookworm) includes Certbot 2.1, Debian 13 (Trixie) ships Certbot 4.0, and Debian 11 (Bullseye) includes 1.12. All versions support the commands and instructions in this guide.

Generate an SSL Certificate with Certbot on Debian

One command handles certificate generation, HTTPS redirection, HSTS (HTTP Strict Transport Security) headers, and OCSP (Online Certificate Status Protocol) stapling all at once.

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. For www-to-non-www or non-www-to-www redirection rules, see how to redirect www in Nginx.

sudo certbot --nginx --agree-tos --redirect --hsts --staple-ocsp --email you@example.com -d yourdomain.com -d www.yourdomain.com

Here is what each flag does:

OptionPurpose
--nginxUses the Nginx plugin to automatically configure your server block
--agree-tosAccepts the Let’s Encrypt terms of service
--redirectSets up a permanent 301 redirect from HTTP to HTTPS
--hstsAdds Strict-Transport-Security header to enforce HTTPS
--staple-ocspEnables OCSP stapling for faster SSL verification
--emailEmail address for renewal reminders and security notices
-dDomain name(s) to include in the certificate

Certbot confirms success with the certificate path 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 Let’s Encrypt Certificate

Confirm the certificate is in place:

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

You can also visit your domain in a browser and check for the padlock icon in the address bar.

Interactive Certbot Certificate Generation on Debian

Certbot also provides an interactive mode that prompts for each configuration option. This approach works well when securing multiple domains or reviewing each setting before applying it.

sudo certbot --nginx

The interactive prompts walk through these decisions:

  1. Email address: Enter your email for renewal reminders and security notices
  2. Terms of service: Input A to agree
  3. EFF newsletter: Input Y or N for Electronic Frontier Foundation updates
  4. Domain selection: Enter the numbers for domains to secure, or leave blank for all detected domains
  5. HTTPS redirect: Select 2 to redirect all HTTP traffic to HTTPS (recommended)

After completing the prompts, Certbot generates the certificate and updates your Nginx configuration automatically.

Test Certbot Certificate Renewal on Debian

Let’s Encrypt certificates expire after 90 days, so automatic renewal is essential. 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)

If the dry run fails, check that port 80 is accessible and your DNS records point to the correct server IP.

Automatic Certbot Renewal on Debian

The Certbot package on Debian includes a systemd timer that automatically attempts certificate renewal twice daily. Debian enables this timer during installation, so no manual cron configuration is needed.

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

If you previously disabled the timer, re-enable it with sudo systemctl enable --now certbot.timer.

The timer runs at midnight and noon each day with a randomized delay to distribute load across Let’s Encrypt servers. Certificates are only renewed when they are within 30 days of expiration. To keep the Certbot package itself patched automatically, configure unattended upgrades on Debian.

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 Nginx SSL Configuration on Debian (Optional)

Certbot’s automatic configuration is secure enough for most sites, but Mozilla’s SSL Configuration Generator recommends tighter session handling, stronger key exchange, and explicit HSTS/OCSP directives for production servers. The tweaks below follow Mozilla’s “Intermediate” profile. You may also want to enable gzip compression in Nginx and configure Upgrade-Insecure-Requests to handle mixed content after SSL is in place.

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 SSL Configuration File

Open your domain’s Nginx server block configuration:

sudo nano /etc/nginx/sites-available/yourdomain.com

Certbot has already added SSL directives and an include line pointing to /etc/letsencrypt/options-ssl-nginx.conf, which contains its default cipher and protocol settings. The tweaks below override some of those defaults for tighter security 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. 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. 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

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;

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. 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 (ssl_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. You can substitute Google DNS (8.8.8.8 8.8.4.4) or your ISP’s resolvers if preferred.

Test and Apply the Nginx 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

Using reload instead of restart applies configuration changes without dropping active connections.

Troubleshoot Certbot and Nginx Issues on Debian

Certificate generation and renewal can fail for a handful of reasons. Each scenario below shows the error, the cause, and how to fix it.

Certbot 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))

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'.

Once DNS and firewall are correct, rerun the Certbot command.

Nginx SSL Module Missing

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 SSL module loaded, rerun Certbot.

OCSP Stapling Fails with “No Resolver Defined”

If Nginx logs show this warning after enabling ssl_stapling:

nginx: [warn] "ssl_stapling" ignored, no resolver defined to resolve ocsp.int-x3.letsencrypt.org

The resolver directive is missing from your server block. Add a DNS resolver inside the server { } block that handles port 443:

resolver 1.1.1.1 1.0.0.1 valid=300s;
resolver_timeout 5s;

Reload Nginx and verify the warning is gone:

sudo nginx -t && sudo systemctl reload nginx

Let’s Encrypt 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

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

Staging certificates are not trusted by browsers but do not count against rate limits.

Remove Certbot and Let’s Encrypt Certificates from Debian

Removing Certbot cleanly means revoking or deleting certificates first, then uninstalling the packages and cleaning up leftover directories.

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 Let’s Encrypt Configuration Files

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

Update the package cache and verify Certbot is fully removed:

sudo apt update
apt-cache policy certbot
certbot:
  Installed: (none)
  Candidate: x.x.x-x
  Version table:
     x.x.x-x 500
        500 http://deb.debian.org/debian <codename>/main amd64 Packages

The Installed: (none) line confirms successful removal.

Frequently Asked Questions

Does Certbot automatically renew Let’s Encrypt certificates on Debian?

Yes. The certbot package on Debian installs a systemd timer that runs twice daily and renews any certificate within 30 days of expiration. No manual cron job or additional configuration is needed.

What is the python3-certbot-nginx package on Debian?

python3-certbot-nginx is the Certbot plugin that integrates with Nginx. It automatically modifies your Nginx server block to add SSL directives, configure redirects, and enable HTTPS without manual file editing.

Can I use Certbot with a wildcard domain on Debian?

Yes, but wildcard certificates require DNS-01 validation instead of HTTP-01. You need a DNS plugin for your provider (such as certbot-dns-cloudflare) and must create a TXT record to prove domain ownership. The --nginx plugin alone cannot issue wildcard certificates.

How do I secure Nginx with Let’s Encrypt on Debian 13?

The process is identical across Debian 13, 12, and 11. Install certbot and python3-certbot-nginx from the default repositories, then run certbot --nginx with your domain. Debian 13 ships Certbot 4.0, which supports the same flags and workflow as earlier versions.

Do I need to install Let’s Encrypt separately from Certbot on Debian 12?

No. Certbot is the official Let’s Encrypt client, so installing the certbot and python3-certbot-nginx packages from the Debian 12 repositories is all you need. There is no separate “Let’s Encrypt” package to install.

Conclusion

Nginx on Debian is now serving all traffic over a valid Let’s Encrypt certificate that renews itself before expiration. HTTPS redirect, HSTS, and OCSP stapling are in place, so connections are both encrypted and fast. To harden the server further, Fail2ban on Debian blocks brute-force attempts and Nginx rate limiting throttles abusive traffic. If you are ready to put the server to use, WordPress with Nginx on Debian is a natural next step.

Search LinuxCapable

Need another guide?

Search LinuxCapable for package installs, commands, troubleshooting, and follow-up guides related to what you just read.

Found this guide useful?

Support LinuxCapable to keep tutorials free and up to date.

Buy me a coffee Buy me a coffee

Before commenting, please review our Comments Policy.
Formatting tips for your comment

You can use basic HTML to format your comment. Useful tags currently allowed:

You type Result
<code>command</code> command
<strong>bold</strong> bold
<em>italic</em> italic
<blockquote>quote</blockquote> quote block

Leave a Comment

We read and reply to every comment - let us know how we can help or improve this guide.

Let us know you are human: