Secure Nginx with Let’s Encrypt on Ubuntu 16.04

s2 0

Let’s Encrypt is a free and open certification authority developed by the Internet Security Research Group (ISRG). Certificates issued by Let’s Encrypt are now classified as trustworthy by almost all browsers.

In this tutorial we show you step by step how to secure your Nginx with Let’s Encrypt using the certbot tool on Ubuntu 16.04.


Make sure you meet the following requirements before proceeding with this tutorial:

  • They have a domain name that points to your public server IP. In this tutorial we will use
  • You installed Nginx by following How to Install Nginx on Ubuntu 16.04.

Install Certbot

Certbot is a utility written in Python that can automate the tasks of getting and renewing Let’s Encrypt SSL certificates and configuring web servers.

First install the software-properties-common Package that the add-apt-repository Tool required to add additional PPAs.

Update the package index and install software-properties-common With:

sudo apt updatesudo apt install software-properties-common

Once the installation is complete, add the certbot PPA repository to your system with the following command:

sudo add-apt-repository ppa:certbot/certbot

Update the package list and install the certbot package:

sudo apt updatesudo apt install certbot

Generate a strong Dh (Diffie-Hellman) group

The Diffie-Hellman key exchange (DH) is a method for the secure exchange of cryptographic keys via an unsecured communication channel. Generate a new set of 2048-bit DH parameters for added security:

sudo openssl dhparam -out /etc/ssl/certs/dhparam.pem 2048
You can resize it up to 4096 bits if you want, but in this case it may take more than 30 minutes to generate, depending on your system enropy.

Obtaining a Let’s Encrypt SSL Certificate

To get an SSL certificate for our domain we use the webroot plugin which works by creating a temporary file to validate the requested domain in the. created ${webroot-path}/.well-known/acme-challenge Directory. The Let’s Encrypt server makes HTTP requests to the temporary file to check whether the requested domain is resolved to the server on which certbot is running.

To make it easier, we’re going to map all HTTP requests for .well-known/acme-challenge in a single directory, /var/lib/letsencrypt.

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

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

To avoid duplicating code, create the following two snippets that we will include in all of our Nginx server block files.

location ^~ /.well-known/acme-challenge/ {
  allow all;
  root /var/lib/letsencrypt/;
  default_type "text/plain";
  try_files $uri =404;
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_prefer_server_ciphers on;

ssl_stapling on;
ssl_stapling_verify on;
resolver 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;

The above excerpt contains the from. recommended chopper Mozilla
, enables OCSP stapling, HTTP Strict Transport Security (HSTS) and enforces few security-oriented HTTP headers.

Once the snippets are created, open the domain server block and add the letsencrypt.conf Cutout as shown below:

server {
  listen 80;

  include snippets/letsencrypt.conf;

Activate the server block by creating a symbolic link from sites-available to sites-enabled:

sudo ln -s /etc/nginx/sites-available/ /etc/nginx/sites-enabled/

Reload the Nginx configuration for the changes to take effect:

sudo systemctl reload nginx

Run the certbot script with the webroot plugin and get the SSL certificate files:

sudo certbot certonly --agree-tos --email [email protected] --webroot -w /var/lib/letsencrypt/ -d -d

When the SSL certificate is successfully obtained, certbot prints the following message:

 - Congratulations! Your certificate and chain have been saved at:
   Your key file has been saved at:
   Your cert will expire on 2018-04-23. 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:
   Donating to EFF:          

Now that we have the certificate files, edit the domain server block as follows:

server {
    listen 80;

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

server {
    listen 443 ssl http2;

    ssl_certificate /etc/letsencrypt/live/;
    ssl_certificate_key /etc/letsencrypt/live/;
    ssl_trusted_certificate /etc/letsencrypt/live/;
    include snippets/ssl.conf;
    include snippets/letsencrypt.conf;

    return 301$request_uri;

server {
    listen 443 ssl http2;

    ssl_certificate /etc/letsencrypt/live/;
    ssl_certificate_key /etc/letsencrypt/live/;
    ssl_trusted_certificate /etc/letsencrypt/live/;
    include snippets/ssl.conf;
    include snippets/letsencrypt.conf;

    # . . . other code

With the above configuration, we are enforcing HTTPS and redirecting that www Version of the domain to the non www Execution.

Reload the Nginx service for the changes to take effect:

sudo systemctl reload nginx

Automatic renewal of the SSL certificate

Let’s Encrypt certificates are valid for 90 days. To automatically renew the certificates before they expire, the certbot package creates a cron job that runs twice a day and automatically renews each certificate 30 days before it expires.

Since we are using the certbot webroot plug-in, after renewing the certificate we also have to reload the nginx service. Attach to it --renew-hook "systemctl reload nginx" to the /etc/cron.d/certbot File looks like this:

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"

To test the renewal process, use the certbot --dry-run switch:

sudo certbot renew --dry-run

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


In this tutorial, you used the Let’s Encrypt client certbot to get SSL certificates for your domain. They also created Nginx snippets to avoid code duplication and configured Nginx to use the certificates. At the end of the tutorial you have set up a cron job for automatic certificate renewal.

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