Setting up Let's Encrypt with Nginx

Posted on Fri 13 December 2019 in development

This blog is a static site generated with Pelican. It's now almost 2020, which means that using https isn't just a good idea, it's pretty-much mandatory. The good news is that setting up Ubuntu, Nginx and Let's Encrypt is incredibly straightforward. Start to finish it only took me a bit over an hour to get everything working together, which was far faster than I expected.

These instructions are based off a walkthrough in Digital Ocean's documentation, which inexplicable can't be directly linked to. Update There are better instructions available on the certbot page. You should probably follow those instructions.

Before we get into the individual steps, there's a handful of assumptions. First, you have a server you can SSH into and it's serving your site via Nginx. You should also have a domain name already setup, no bare IP addresses.

sudo add-apt-repository ppa:certbot/certbot
sudo apt-get update
sudo apt-get install certbot

In order to validate that you own the domain that you're attempting to configure the certificate for, you'll need to expose the .well-known path. Modify your sites-available/default Nginx configuration to add the following location.

location ~ /.well-known { 
    allow all; 
}

This directive ensures that requests to /.well-known will succeed. The is necessary to support the ACME Challenge from Let's Encrypt. This challenge is used to verify that you own the domain name.

Now that Nginx is configured to allow certification of your domain, it's time to run the certbot command to validate the domain.

sudo certbot certonly --webroot --webroot-path=/var/www -d example.com -d www.example.com

This command will validate that you own the domain for which you are requesting a certificate, place that certificate in /etc/letsencrypt. After the certificate is generated, you'll need to configure Nginx to use TLS/SSL. The recommended way to do this is to generate Nginx "snippets" containing the required configuration.

sudo vi /etc/nginx/snippets/ssl-example.com.conf

Inside the snippet file, add the following lines, replacing your domain name as required.

ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

Now we need to create a snippet for setting strong encryption settings. Run sudo vi /etc/nginx/snippets/ssl-params.conf to create the file. Below I'll include how I have my file setup, but you should probably reference raymii.org for up-to-date instructions on configuring SSL, as these recommendations will change over time.

ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on; 
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384;
ssl_dhparam /etc/ssl/certs/dhparam.pem;
ssl_ecdh_curve secp384r1; 
ssl_session_cache shared:SSL:10m; 
ssl_session_tickets off; 

ssl_stapling on; 
ssl_stapling_verify on; 
resolver 8.8.8.8 8.8.4.4 valid=300s; 
resolver_timeout 5s; 

# disable HSTS header for now 
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"; 
add_header X-Frame-Options DENY; 
add_header X-Content-Type-Options nosniff; 

That's about it. Overall I was pleased with how easy the setup was. Having an unencrypted server is no longer an option for me.