Let’s Encrypt is a free and open certificate authority developed by  the Internet Security Research Group (ISRG). Certificates issued by  Let’s Encrypt are trusted by almost all browsers today.

In this  tutorial, we will explain how to use the Certbot tool to obtain a free  SSL certificate for Nginx on Debian 9. 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 continuing with this tutorial:

  • Logged in as a user with sudo privileges.
  • Have a domain name pointing to your public server IP. We’ll use example.com.
  • Have Nginx installed by following these instructions
  • You have a server block for your domain. You can follow these instructions for details on how to create one.

Install Certbot

Certbot  is a fully featured and easy to use tool that can automate 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.

Update the packages list and install the certbot package:

sudo apt update

Generate Strong 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

If  you like you can change the size up to 4096 bits, but in that case, 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 our domain we’re going to use the Webroot  plugin that 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.

The following commands will create the directory and make it writable for the Nginx server.

sudo mkdir -p /var/lib/letsencrypt/.well-known

To avoid duplicating code create the following two snippets that will be included in all our 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;
}

Copy

Create the second snippet ssl.conf that 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 TLSv1.1 TLSv1.2;
ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS';
ssl_prefer_server_ciphers on;

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=15768000; includeSubdomains; preload";
add_header X-Frame-Options SAMEORIGIN;
add_header X-Content-Type-Options nosniff;

Copy

Once done, open the domain server block file and include the letsencrypt.conf snippet as shown below:

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

/etc/nginx/sites-available/example.com.conf

server {
  listen 80;
  server_name example.com www.example.com;

  include snippets/letsencrypt.conf;
}

Copy

Enable the new server block by creating a symbolic link to the sites-enabled directory:

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

Restart the Nginx service for the changes to take effect:

sudo systemctl restart nginx

You can now run Certbot with the webroot plugin and obtain the SSL certificate files by issuing:

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:
 - 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 2018-07-28. 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"
 - Your account credentials have been saved in your Certbot
   configuration directory at /etc/letsencrypt. You should make a
   secure backup of this folder now. This configuration directory will
   also contain certificates and private keys obtained by Certbot so
   making regular backups of this folder is ideal.
 - 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

Next, edit the domain server block as follows:

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

/etc/nginx/sites-available/example.com.conf

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
}

Copy

With the configuration above we are forcing HTTPS and redirecting from www to non-www version.

Reload the Nginx service for the changes to take effect:

sudo systemctl reload nginx

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 renew any certificate 30  days before its expiration.

Since we are using the certbot webroot plug-in once the certificate is renewed we also have to reload the nginx service. 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"

Copy

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, you used the Let’s Encrypt client,  certbot to generate SSL certificates for your domain. You have also  created Nginx snippets to avoid duplicating code and configured Nginx to  use the certificates. At the end of the tutorial you have set up a  cronjob for automatic certificate renewal.

If you want to learn more about how to use Certbot, their documentation is a good starting point.