Secure Nginx with Let's Encrypt on Debian 10 Linux

Posted 

5 min read

Let's Encrypt is a free, automated, and open certificate authority developed by the Internet Security Research Group (ISRG) that provides free SSL certificates.

Certificates issued by Let's Encrypt are trusted by all major browsers and valid for 90 days from the issue date.

This tutorial shows how to install a free Let's Encrypt SSL certificate on Debian 10, Buster running Nginx as a web server. We’ll also show how to configure Nginx to use the SSL certificate and enable HTTP/2.

Prerequisites

Ensure the following prerequisites are met before proceeding with the guide:

  • Logged in as root or user with sudo privileges.
  • The domain for which you want to obtain the SSL certificate must point to your public server IP. We'll use example.com.
  • Nginx installed.

Installing Certbot

We'll use the certbot tool to obtain and renew the certificates.

Certbot is a fully-featured and easy to use tool that automates the tasks for obtaining and renewing Let’s Encrypt SSL certificates and configuring web servers to use the certificates.

The certbot package is included in the default Debian repositories. Run the following commands to install certbot:

sudo apt update
sudo apt install certbot

Generating Dh (Diffie-Hellman) Group

Diffie–Hellman key exchange (DH) is a method of securely exchanging cryptographic keys over an unsecured communication channel.

We're going to generate a new set of 2048 bit DH parameters to strengthen the security:

sudo openssl dhparam -out /etc/ssl/certs/dhparam.pem 2048

You can also change the size up to 4096 bits, but the generation may take more than 30 minutes depending on the system entropy.

Obtaining a Let's Encrypt SSL certificate

To obtain an SSL certificate for the domain, we're going to use the Webroot plugin. It works by creating a temporary file for validating the requested domain in the ${webroot-path}/.well-known/acme-challenge directory. The Let’s Encrypt server makes HTTP requests to the temporary file to validate that the requested domain resolves to the server where certbot runs.

We're going to map all HTTP requests for .well-known/acme-challenge to a single directory, /var/lib/letsencrypt.

Run the following commands to create the directory and make it writable for the Nginx server:

mkdir -p /var/lib/letsencrypt/.well-known
chgrp www-data /var/lib/letsencrypt
chmod g+s /var/lib/letsencrypt

To avoid duplicating code, we'll create two snippets that will be included in all Nginx server block files.

Open your text editor and create the first snippet, letsencrypt.conf:

sudo nano /etc/nginx/snippets/letsencrypt.conf
/etc/nginx/snippets/letsencrypt.conf
location ^~ /.well-known/acme-challenge/ {
  allow all;
  root /var/lib/letsencrypt/;
  default_type "text/plain";
  try_files $uri =404;
}

The second snippet ssl.conf includes the chippers recommended by Mozilla, enables OCSP Stapling, HTTP Strict Transport Security (HSTS), and enforces few security‑focused HTTP headers.

sudo nano /etc/nginx/snippets/ssl.conf
/etc/nginx/snippets/ssl.conf
ssl_dhparam /etc/ssl/certs/dhparam.pem;

ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
ssl_session_tickets off;

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;
ssl_prefer_server_ciphers off;

ssl_stapling on;
ssl_stapling_verify on;

resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 30s;

add_header Strict-Transport-Security "max-age=63072000" always;
add_header X-Frame-Options SAMEORIGIN;
add_header X-Content-Type-Options nosniff;

Once done, open the [domain server block](https://linuxize.com/post/how-to-set-up-nginx-server-blocks-on-debian-9/ file and include the letsencrypt.conf snippet as shown below:

sudo nano /etc/nginx/sites-available/example.com
/etc/nginx/sites-available/example.com
server {
  listen 80;
  server_name example.com www.example.com;

  include snippets/letsencrypt.conf;
}

Create a symbolic link to the sites-enabled directory to enable the domain server block:

sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/

Restart the Nginx service for the changes to take effect:

sudo systemctl restart nginx

You're now ready to obtain the SSL certificate files by running the following command:

sudo certbot certonly --agree-tos --email admin@example.com --webroot -w /var/lib/letsencrypt/ -d example.com -d www.example.com

If the SSL certificate is successfully obtained, the following message will be printed on your terminal:

IMPORTANT NOTES:
IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at:
   /etc/letsencrypt/live/example.com/fullchain.pem
   Your key file has been saved at:
   /etc/letsencrypt/live/example.com/privkey.pem
   Your cert will expire on 2020-02-22. To obtain a new or tweaked
   version of this certificate in the future, simply run certbot
   again. To non-interactively renew *all* of your certificates, run
   "certbot renew"
 - If you like Certbot, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le

Edit the domain server block and include the SSL certificate files as follows:

sudo nano /etc/nginx/sites-available/example.com
/etc/nginx/sites-available/example.com
server {
    listen 80;
    server_name www.example.com example.com;

    include snippets/letsencrypt.conf;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    server_name www.example.com;

    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
    include snippets/ssl.conf;
    include snippets/letsencrypt.conf;

    return 301 https://example.com$request_uri;
}

server {
    listen 443 ssl http2;
    server_name example.com;

    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
    include snippets/ssl.conf;
    include snippets/letsencrypt.conf;

    # . . . other code
}

The configuration above tells Nginx to redirect from HTTP to HTTPS and from www to non-www version.

Restart or reload the Nginx service for the changes to take effect:

sudo systemctl restart nginx

Open your website using https://, and you'll notice a green lock icon.

If you test your domain using the SSL Labs Server Test, you'll get an A+ grade, as shown in the image below:

Auto-renewing Let's Encrypt SSL certificate

Let's Encrypt's certificates are valid for 90 days. To automatically renew the certificates before they expire, the certbot package creates a cronjob that runs twice a day and automatically renews any certificate 30 days before its expiration.

On renewal the nginx service must be reloaded for the server to load the certificate. Append --renew-hook "systemctl reload nginx" to the /etc/cron.d/certbot file so as it looks like this:

sudo nano /etc/cron.d/certbot
/etc/cron.d/certbot
0 */12 * * * root test -x /usr/bin/certbot -a \! -d /run/systemd/system && perl -e 'sleep int(rand(3600))' && certbot -q renew --renew-hook "systemctl reload nginx"

Test the automatic renewal process, by running this command:

sudo certbot renew --dry-run

If there are no errors, it means that the renewal process was successful.

Conclusion

Having an SSL certificate is a must nowadays. It secures your website, increases SERP ranking position, and allows you to enable HTTP/2 on your web server.

In this tutorial, we have shown you how to generate and renew SSL certificates using the certbot script. We have also shown you how to configure Nginx to use the certificates.

To learn more about Certbot, visit the Certbot documentation.