How to secure your website with Nginx and let's encrypt ?

Introduction

If you have one and it doesn't support HTTPS yet, you should get up to speed. Indeed, more and more of the web's traffic is being delivered over HTTPS. Navigators are increasingly discriminating against unsecure traffic and it's a good thing for user. And not so bad for administrators since Let's Encrypt made it free to issue a certificate for your domain.

In this article, I'm going to tell you how I enabled HTTPS on my websites served with Nginx.

How it works

We're going to use the webroot plugin of certbot to issue our certificate.

On Ubuntu, cerbot is packaged as letsencrypt, so if you don't have it yet:

apt-get install letsencrypt

To get a certificate, we will need to prove that the host asking a certificate for www.example.com is the same that www.example.com points at.

For that, cerbot will create a directory that we have to expose on the domain.

Let's encrypt will request a file in this directory to verify the host's identify. If the challenge is successful, we will get our certificate that we can use to secure our traffic.

Modify Nginx to answer challenge

First, we will setup the location letsencrypt will ask for when completing the challenge.

In your domain's server block, put the following lines:

location /.well-known/ {
    root /var/lib/letsencrypt/;
}

.well-known/ is the name of the directory created by certbot when seting up the challenge and we have to expose it on the root of the domain.

/var/lib/letsencrypt/ is the place where we'll tell cerbot to setup the challenge. You can use another one, but make sure to substitute it when running cerbot.

Then, reload nginx to take account of this change.

service nginx reload

Use let's encrypt to get a certificate

Now that Nginx is ready, we can get our certifcate with cerbot. Run the following command, replacing blog.shir0.fr with your own domain name.

letsencrypt certonly --webroot -w /var/lib/letsencrypt -d blog.shir0.fr

If it succeeds, it will create the certificate and its key at /etc/letsencrypt/live/blog.shir0.fr/fullchain.pem and /etc/letsencrypt/live/blog.shir0.fr/privkey.pem. Again, with the domain name replaced.

These are valid for 90 days. You can either renew them by hand later or automate the renewal, which we'll see in a later section.

Enable HTTPS

To enable HTTPS, you nginx server must listen on port 443 and know the location of your certificate files. To do that, add the following lines to the server's block and reload nginx.:

listen 443 ssl http2;
listen [::]:443 ssl http2;

ssl_certificate /etc/letsencrypt/live/blog.shir0.fr/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/blog.shir0.fr/privkey.pem;

With this, you should be able to access your website through HTTPS. Simply change http by https at the beginning of the URL.

Force HTTPS

This step is optionnal, but you can force visitors to use HTTPS to read your website. Simply add the following lines in server's configuration.:

if ($scheme != "https") {
        return 301 https://$host$request_uri;
}

Again, reload your nginx server for the change to take effect.

Automatic renewal

We will use the crontab to automate the renewal of our certificate. We'll keep the same command used to issue the certificate and add the --keep option. it will inform cerbot to keep the certificate if they are not expiring (They are marked as expiring after 60 days).

We will run this command daily. This way, the first try of renewal will happen the 61th day. If it succeeds, a new valid certificate will be issued for 90 days. If it fails due to some unavailability or some network errors, it will have ~29 try before it is a problem, which should not happen.

Change your crontab (with crontab -e to add the following line.:

0 0 * * * letsencrypt certonly --webroot -w /var/lib/letsencrypt -d shir0.fr --keep

The two zeros at the beginning are respectively hour and minutes. You can change them at your will.

If something doesn't work