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

Last updated Wednesday, May 20, 2026 11:06 am Joshua James 8 min read

HTTPS is the point where an Nginx site stops exposing logins, cookies, and form data over plain HTTP. On Debian, the shortest maintained path is the packaged Certbot client with the Nginx plugin: Certbot proves domain control, installs a Let’s Encrypt SSL/TLS certificate, updates the Nginx server block, and enables a systemd renewal timer.

This workflow uses the Debian archive packages for Debian 13 (Trixie), Debian 12 (Bookworm), and Debian 11 (Bullseye). Debian 11 is under LTS until August 31, 2026, so use Debian 12 or 13 for new public servers when possible and plan older Bullseye hosts around the Debian release lifecycle.

Prerequisites for Certbot and Nginx on Debian

Certbot’s Nginx plugin needs a working HTTP site before it can request a certificate. Confirm these pieces first:

Let’s Encrypt HTTP-01 validation reaches your server over port 80. Keep the HTTP site reachable until Certbot finishes, even if the final goal is an HTTPS-only redirect.

Install Certbot and the Nginx Plugin on Debian

Refresh APT metadata before installing Certbot. Apply pending system upgrades during a normal maintenance window if the server has not been patched recently.

sudo apt update

Install Certbot and the Debian Nginx plugin package:

sudo apt install certbot python3-certbot-nginx

The package names are stable across Debian 13, 12, and 11, but the packaged Certbot branch depends on the release. Debian 13 currently provides Certbot 4.0, Debian 12 provides Certbot 2.1, and Debian 11 provides Certbot 1.12. Package revisions can change with Debian security updates.

PackageRole
certbotProvides the Certbot CLI, account handling, certificate issuance, renewal, and the systemd timer
python3-certbot-nginxAdds the Nginx authenticator and installer plugin used by --nginx
nginxInstalled as a dependency if Nginx is missing, though production sites should already have a working server block

On Debian 12, searches for python3-certbot-nginx refer to this same plugin package from the Bookworm main repository. There is no separate letsencrypt package to install for the Nginx workflow.

The official Certbot Nginx plugin documentation describes the --nginx plugin as the normal path for most Nginx configurations and recommends backing up Nginx configuration before using it.

Confirm the installed Certbot version. On Debian 13, the output currently resembles:

certbot --version
certbot 4.0.0

On Debian 12 and Debian 11, the version number is older, but the Nginx plugin, redirect, HSTS, and OCSP stapling flags used here are available.

Verify that Certbot sees the Nginx plugin:

certbot plugins --text
* nginx
Description: Nginx Web Server plugin

Generate a Let’s Encrypt SSL Certificate with Certbot

The Nginx plugin can request the certificate and update the matching server block in one run. Replace you@example.com, example.com, and www.example.com with your real email address and hostnames.

The --hsts flag tells browsers to remember HTTPS for the domain. Omit --hsts during early testing if you might need to return the domain to plain HTTP.

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

Request a single certificate for the apex domain and the www hostname by adding another -d value. Handle the canonical hostname decision separately with www and non-www redirects in Nginx.

sudo certbot --nginx --non-interactive --agree-tos --redirect --hsts --staple-ocsp --email you@example.com -d example.com -d www.example.com
OptionPurpose
--nginxUses the Nginx plugin for authentication and installation
--non-interactiveRuns without prompts when all required values are supplied
--agree-tosAccepts the ACME subscriber agreement
--redirectRedirects HTTP requests to HTTPS
--hstsAdds a Strict-Transport-Security header
--staple-ocspEnables OCSP stapling when the certificate supports it
--emailSets the account recovery and notification address
-dAdds each hostname to the certificate request

A successful run reports the saved certificate and key paths:

Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/example.com/fullchain.pem
Key is saved at:         /etc/letsencrypt/live/example.com/privkey.pem

Verify the Let’s Encrypt Certificate and Nginx Config

List certificates managed by Certbot:

sudo certbot certificates

Then test the Nginx configuration that Certbot modified:

sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

Check the live HTTPS response from another shell or workstation after DNS has propagated:

curl -I https://example.com

Look for an HTTP success or redirect status and, when --hsts was used, a strict-transport-security header.

Use Interactive Certbot Mode on Debian

Interactive mode is useful when you want Certbot to detect Nginx server blocks and prompt for the domains to secure.

sudo certbot --nginx

Follow the prompts for the email address, subscriber agreement, domain selection, and redirect choice. Choose the redirect option when the site should send all HTTP traffic to HTTPS.

Test Certbot Renewal on Debian

Let’s Encrypt certificates are short-lived, so test renewal before depending on unattended automation. Run the dry run after at least one certificate exists:

sudo certbot renew --dry-run

A successful dry run ends with a simulated renewal success message:

Processing /etc/letsencrypt/renewal/example.com.conf
Simulating renewal of an existing certificate for example.com
Congratulations, all simulated renewals succeeded:
  /etc/letsencrypt/live/example.com/fullchain.pem (success)

The dry run uses the Let’s Encrypt staging service and can temporarily modify, test, and roll back web server configuration. If it fails, fix DNS, firewall, or Nginx syntax before waiting for the next automatic renewal attempt.

Verify Automatic Certbot Renewal on Debian

The Debian Certbot package installs a systemd timer and enables it during package installation. Confirm the timer is enabled and active:

systemctl is-enabled certbot.timer
systemctl is-active certbot.timer
enabled
active

Inspect the packaged schedule when you need the exact timer settings:

systemctl cat certbot.timer
[Timer]
OnCalendar=*-*-* 00,12:00:00
RandomizedDelaySec=43200
Persistent=true

The timer schedules attempts around midnight and noon with a randomized delay, which spreads renewal traffic across the day. Certbot renews only certificates close enough to expiration. To keep Certbot itself patched through normal APT updates, use unattended upgrades on Debian.

Update Certbot Packages on Debian

Certificate renewal keeps existing certificates current, but the Certbot packages still receive fixes through APT. For a manual package refresh, update metadata and upgrade only the installed Certbot packages:

sudo apt update
sudo apt install --only-upgrade certbot python3-certbot-nginx

Use normal full-system patching for the rest of the web stack, including Nginx, OpenSSL libraries, and systemd.

Review Nginx SSL Settings After Certbot

Certbot already writes the certificate paths and includes its maintained Nginx SSL options file. For most sites, keep those generated directives and use Mozilla’s SSL Configuration Generator only when you intentionally need a different TLS profile.

Do not duplicate TLS directives in the same Nginx context. If /etc/letsencrypt/options-ssl-nginx.conf already owns a setting, edit only one place or Nginx may reject the configuration.

Inspect Certbot SSL Directives

Check the server block Certbot modified:

sudo grep -E 'ssl_certificate|ssl_certificate_key|options-ssl-nginx|ssl_dhparam|Strict-Transport-Security|ssl_stapling' /etc/nginx/sites-available/example.com
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

If the file name differs, use the server block that owns your domain. Certbot may add extra lines when you use flags such as --hsts or --staple-ocsp.

Add an OCSP Resolver Only When Needed

If Nginx warns that OCSP stapling has no resolver, add a resolver inside the HTTPS server { } block:

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

This example uses Cloudflare DNS. Use your provider’s recursive resolvers if the server has an internal DNS policy.

Apply SSL Configuration Changes Safely

Test Nginx before every reload:

sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

Reload Nginx after the syntax test passes:

sudo systemctl reload nginx

For adjacent hardening after HTTPS works, consider gzip compression in Nginx, Upgrade-Insecure-Requests in Nginx, and Nginx security headers.

Use DNS Validation for Wildcard Certificates

The Nginx plugin handles HTTP-01 validation for normal hostnames such as example.com and www.example.com. Wildcard certificates such as *.example.com require DNS-01 validation, which means adding a TXT record manually or using a DNS provider plugin. The --nginx plugin alone cannot issue a wildcard certificate.

Keep wildcard automation separate from the basic Nginx flow because each DNS provider uses different credentials, plugin packages, and cleanup requirements.

Troubleshoot Certbot and Nginx Issues on Debian

Most Certbot failures come from DNS, port 80 reachability, Nginx syntax, or repeated production requests during testing. Work through the matching symptom and rerun Certbot only after the failed layer is fixed.

Certbot Domain Validation Fails

A failed HTTP-01 challenge means Let’s Encrypt could not retrieve the temporary validation file from the domain.

Challenge failed for domain example.com
Type:   unauthorized
Detail: Invalid response from http://example.com/.well-known/acme-challenge/...

Confirm the domain resolves to the server:

getent hosts example.com
203.0.113.10    example.com

Check that Nginx is listening on HTTP:

sudo ss -tlnp | grep ':80'
LISTEN 0 511 0.0.0.0:80 0.0.0.0:* users:(("nginx",pid=1234,fd=6))

If UFW controls the host firewall, confirm HTTP and HTTPS are allowed:

sudo ufw status
80/tcp                     ALLOW       Anywhere
443/tcp                    ALLOW       Anywhere

Allow the ports when they are missing:

sudo ufw allow 80/tcp
sudo ufw allow 443/tcp

Use Nginx access and error logs when DNS and firewall checks pass but the challenge URL still returns the wrong site or status code.

Nginx Command or SSL Module Checks Fail

Debian places the Nginx binary under /usr/sbin, which may not be in a regular user’s PATH during SSH sessions. Use sudo nginx for version and syntax checks:

sudo nginx -V 2>&1 | grep -- '--with-http_ssl_module'
--with-http_ssl_module

The Debian archive Nginx packages include SSL support. If the flag is missing, the server is probably using a custom or third-party Nginx build. Install the Debian package or rebuild the custom Nginx binary with SSL support before rerunning Certbot.

sudo apt install nginx

OCSP Stapling Reports No Resolver

When OCSP stapling is enabled without a resolver, Nginx can log a warning like this:

nginx: [warn] "ssl_stapling" ignored, no resolver defined

Add a resolver to the HTTPS server block, then test and reload:

resolver 1.1.1.1 1.0.0.1 valid=300s;
resolver_timeout 5s;
sudo nginx -t
sudo systemctl reload nginx

Let’s Encrypt Rate Limit Errors

Repeated production requests can trigger Let’s Encrypt rate limits, especially when the same domain set is requested many times during troubleshooting.

too many certificates already issued for exact same set of identifiers

Use a dry run while testing new issuance, and review the Let’s Encrypt rate limits before repeating production requests:

sudo certbot certonly --nginx --dry-run --agree-tos --email you@example.com -d example.com -d www.example.com

Wait for the rate limit to refill when production issuance is already blocked. Changing domain sets can create a different certificate request, but it should not be used to hide an unresolved validation problem.

Remove Certbot and Let’s Encrypt Certificates from Debian

Remove certificates before uninstalling Certbot when the domain is being retired or moved to another certificate workflow. Keep Nginx installed unless the web server itself is also being removed.

Revoke and Delete a Certificate

Revoke a certificate only when the private key was exposed or the domain should stop trusting that certificate immediately. For routine cleanup on a server you control, deletion is usually enough.

Revoke and then delete a certificate by Certbot’s certificate name:

sudo certbot revoke --cert-name example.com
sudo certbot delete --cert-name example.com

Delete the certificate without revoking it when the key was not compromised:

sudo certbot delete --cert-name example.com

Remove Certbot Packages

Disable the renewal timer and remove Certbot packages:

sudo systemctl disable --now certbot.timer
sudo apt remove --purge certbot python3-certbot-nginx

If APT reports unused dependencies afterward, review the package list before removing them. Do not remove Nginx or shared Python packages if other services still depend on them.

Clean Up Let’s Encrypt Directories

The next commands permanently delete all Certbot certificates, renewal files, and logs on the server. Skip them when other domains on the same host still use Certbot.

sudo rm -rf /etc/letsencrypt
sudo rm -rf /var/lib/letsencrypt
sudo rm -rf /var/log/letsencrypt

Remove SSL directives that point at deleted certificate paths from the Nginx server block, then test and reload:

sudo nginx -t
sudo systemctl reload nginx

Confirm APT no longer has Certbot installed:

apt-cache policy certbot python3-certbot-nginx

The Installed: (none) line for each package confirms package removal.

Conclusion

Nginx can now serve the Debian site over a Let’s Encrypt certificate, redirect HTTP to HTTPS, and renew automatically through the packaged Certbot timer. Keep DNS and port 80 healthy for future renewals, test Nginx before every reload, and add only the SSL directives the site actually needs. For the next hardening pass, install Fail2ban on Debian, add Nginx rate limiting, or deploy an application such as WordPress with Nginx on Debian.

Follow LinuxCapable

Want more LinuxCapable guides in Google?

Add LinuxCapable as a preferred source so Google can show more of our fresh Linux tutorials in Top Stories and From your sources when relevant.

Add LinuxCapable as a preferred source on Google
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 coffeeBuy 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 in published comments:

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

Got a Question or Feedback?

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

Let us know you are human: