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:
- A Debian server with root or sudo access; if needed, add a user to sudoers on Debian before changing packages or services
- Nginx installed and running; use install Nginx on Debian, or set up a full LEMP stack on Debian if the site also needs PHP and MariaDB
- A registered domain name with DNS A and, when used, AAAA records pointing to the server
- An Nginx server block for the domain under
/etc/nginx/sites-available/; review Nginx server blocks and virtual hosts if the site is still using only the default page - Ports 80 and 443 allowed through the active firewall; use configure UFW on Debian when UFW manages the host firewall
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.
| Package | Role |
|---|---|
certbot | Provides the Certbot CLI, account handling, certificate issuance, renewal, and the systemd timer |
python3-certbot-nginx | Adds the Nginx authenticator and installer plugin used by --nginx |
nginx | Installed 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
--hstsflag tells browsers to remember HTTPS for the domain. Omit--hstsduring 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
| Option | Purpose |
|---|---|
--nginx | Uses the Nginx plugin for authentication and installation |
--non-interactive | Runs without prompts when all required values are supplied |
--agree-tos | Accepts the ACME subscriber agreement |
--redirect | Redirects HTTP requests to HTTPS |
--hsts | Adds a Strict-Transport-Security header |
--staple-ocsp | Enables OCSP stapling when the certificate supports it |
--email | Sets the account recovery and notification address |
-d | Adds 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.confalready 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.


Formatting tips for your comment
You can use basic HTML to format your comment. Useful tags currently allowed in published comments:
<code>command</code>command<strong>bold</strong><em>italic</em><blockquote>quote</blockquote>