How to Secure Nginx with Let’s Encrypt SSL on Debian 12

Secure Nginx with Let's Encrypt SSL on Debian 12 – DevAnswers.net

This guide covers Debian 12. Read the Debian 13 edition.

We may earn a referral fee at no additional cost to you when you shop through the links on this page.

Debian 12 (Bookworm), released June 2023, is a stable release with approximately five years of security support. This guide uses Certbot with the Nginx plugin to obtain and configure a free Let’s Encrypt SSL certificate, bringing encrypted HTTPS connections to your Nginx web server.

The following steps cover installing Certbot, obtaining a free Let’s Encrypt SSL certificate for your domain, configuring Nginx for HTTPS with automatic HTTP redirection, enabling HSTS security headers, and setting up automatic certificate renewal.

Prerequisites 

Before you begin, ensure you have:

  • A server running Debian 12 (Bookworm) with a non-root user that has sudo privileges. See our initial server setup guide for Debian 12 (Bookworm) if needed.
  • Nginx installed with a server block configured. Follow our How to Install Nginx on Debian 12 (Bookworm) guide first.
  • A registered domain name with its DNS A record pointing to your server’s public IP address. Both example.com and www.example.com should resolve to the server. Let’s Encrypt does not issue certificates for bare IP addresses.
  • Ports 80 and 443 open in your firewall. If you followed the Nginx guide and allowed Nginx Full in UFW, both ports are already open.

Installing Certbot 

Certbot is the official Let’s Encrypt client that automates certificate issuance and Nginx configuration. Install it along with the Nginx plugin:

Terminal
sudo apt update && sudo apt install -y certbot python3-certbot-nginx

Verify the installation:

Terminal
certbot --version

You should see the Certbot version number in the output, confirming a successful installation.

Obtaining an SSL Certificate 

Before requesting a certificate, verify that your domain resolves to your server’s IP address:

Terminal
host example.com

The output should show your server’s public IP. If it doesn’t, update your DNS A record at your domain registrar and wait for propagation before continuing.

DNS must be pointed correctly. Let’s Encrypt verifies domain ownership by making an HTTP request to your server on port 80. If your domain doesn’t resolve to this server, the certificate request will fail. DNS propagation can take up to 48 hours after changes.

Run Certbot with the Nginx plugin to obtain and install your certificate. Replace example.com with your actual domain:

Terminal
sudo certbot --nginx -d example.com -d www.example.com
If you only use one domain (no www subdomain), omit the second -d flag. You can also secure multiple subdomains by adding additional -d flags (e.g., -d blog.example.com).

Certbot will prompt you for the following:

  1. Email address — for certificate expiry notifications and account recovery. Enter a valid email.
  2. Terms of Service — you must agree (Y) to proceed.
  3. EFF mailing list — optional. Enter Y or N based on your preference.

If successful, you will see output confirming the certificate was issued and deployed:

Output
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
This certificate expires on 2026-05-20.

Deploying certificate
Successfully deployed certificate for example.com
Successfully deployed certificate for www.example.com

Congratulations! You have successfully enabled HTTPS on
https://example.com and https://www.example.com
What Certbot did behind the scenes:
  • Obtained a trusted certificate from Let’s Encrypt via an HTTP-01 challenge
  • Added listen 443 ssl and certificate paths to your Nginx server block
  • Configured HTTP-to-HTTPS redirection with a separate server block on port 80
  • Applied recommended TLS settings from /etc/letsencrypt/options-ssl-nginx.conf
  • Reloaded Nginx to apply all changes
Certbot failed? Common causes:
  • Domain not resolving: Verify your DNS A record points to this server’s IP
  • Port 80 blocked: Ensure sudo ufw status shows Nginx Full allowed
  • No matching server block: Certbot needs an Nginx server block with a server_name matching your domain
  • Nginx config error: Run sudo nginx -t to check for syntax errors before retrying
  • Rate limit exceeded: Let’s Encrypt allows 5 duplicate certificates per domain per week. Wait and retry later.

Verifying HTTPS 

Open your browser and visit https://example.com. You should see your site served over HTTPS with a padlock icon in the address bar.

From the command line, verify the HTTPS response headers:

Terminal
curl -sI https://example.com

You should see HTTP/2 200 (or HTTP/1.1 200 OK) at the top of the response.

Next, verify that HTTP requests are automatically redirected to HTTPS:

Terminal
curl -sI http://example.com

You should see a 301 Moved Permanently response with a Location header pointing to https://example.com/. This confirms that all HTTP traffic is being redirected to the secure HTTPS version.

For a comprehensive security assessment, test your domain at SSL Labs. With Certbot’s default TLS configuration, you should receive an A or A+ rating.

Strengthening SSL Security 

Certbot’s default configuration provides solid SSL security, including modern TLS protocols and cipher suites managed via /etc/letsencrypt/options-ssl-nginx.conf. To further protect your site, enable HTTP Strict Transport Security (HSTS), which instructs browsers to always use HTTPS even if a user types http:// in the address bar.

Open the Nginx server block that Certbot modified:

Terminal
sudo nano /etc/nginx/sites-available/example.com

Find the server block that listens on port 443 (the one Certbot added the SSL directives to) and add the HSTS header inside it:

example.com
server {
    listen 443 ssl; # managed by Certbot
    server_name example.com www.example.com;
    root /var/www/example.com;

    # HSTS — force browsers to use HTTPS for one year
    add_header Strict-Transport-Security "max-age=31536000" always;

    # ... rest of your configuration ...
}

Save and exit (press CTRL + X, press Y, then press ENTER)

Test the configuration and reload Nginx:

Terminal
sudo nginx -t && sudo systemctl reload nginx

Verify the HSTS header is present:

Terminal
curl -sI https://example.com | grep -i strict

You should see:

Output
strict-transport-security: max-age=31536000
The max-age=31536000 value tells browsers to remember the HTTPS-only policy for one year (31,536,000 seconds). Once your site is stable on HTTPS, you can optionally add includeSubDomains and submit your domain to the HSTS preload list for browser-level enforcement. Only do this when you are certain all subdomains support HTTPS.

Automatic Certificate Renewal 

Let’s Encrypt certificates are valid for 90 days. Certbot installs a systemd timer that automatically checks for renewal twice daily and renews any certificate within 30 days of expiry.

Verify the renewal timer is active:

Terminal
sudo systemctl status certbot.timer

You should see active (waiting) in the output, along with the next scheduled run time.

Test the renewal process with a dry run (simulates renewal without making any changes):

Terminal
sudo certbot renew --dry-run

If the dry run completes without errors, automatic renewal is working correctly. You should see Congratulations, all simulated renewals succeeded in the output.

Certbot handles everything automatically. You do not need to set up cron jobs or manually renew certificates. If renewal fails, Certbot sends a notification to the email address you provided during setup.

Important File Locations 

Key paths for managing SSL certificates on Debian 12 (Bookworm):

  • Certificate (full chain): /etc/letsencrypt/live/example.com/fullchain.pem — The complete certificate chain sent to browsers.
  • Private Key: /etc/letsencrypt/live/example.com/privkey.pem — Your certificate’s private key. Keep this secure.
  • Nginx Server Block: /etc/nginx/sites-available/example.com — Your site configuration with Certbot’s SSL additions.
  • Renewal Configuration: /etc/letsencrypt/renewal/example.com.conf — Settings used for automatic renewal.
  • SSL Options: /etc/letsencrypt/options-ssl-nginx.conf — Certbot-maintained TLS protocol and cipher settings.
  • Certbot Log: /var/log/letsencrypt/letsencrypt.log — Detailed log for troubleshooting certificate operations.

Conclusion 

Your Debian 12 (Bookworm) server is now secured with a free Let’s Encrypt SSL certificate, automatic HTTP-to-HTTPS redirection, HSTS security headers, and automated 90-day certificate renewal.

With SSL in place, your LEMP stack is production-ready. Your site benefits from encrypted connections, improved search engine rankings, and browser trust indicators. Consider deploying your application (WordPress, Laravel, or another PHP framework) or exploring advanced configurations such as HTTP/2, OCSP stapling, and Content Security Policy headers.

That’s the build doneOptional next: How to Install WordPress on Nginx on Debian 12
Support DevAnswers
Written, tested and maintained by one developer. Your support keeps these guides accurate and ad-light.
Buy me a coffee

Comments

Leave a reply