Setting up Let's Encrypt with multi-vhost Nginx on Ubuntu 14.04

With Let’s Encrypt considered mature for a while now, it was finally time for me to dive in and do the HTTPS dance with some of my sites. It’s a straightforward enough process for single-site hosts, but setting it up on a server hosting multiple HTTP/HTTPS sites requires a little more tinkering. Here’s my brief tutorial. This guide assumes you’re running Ubuntu 14.04 and using Nginx as your web server.

Configuring the default vhost: A crucial detail I missed the first time I tried this is that if you don’t have your default vhost correctly configured – even if you never access the server via that vhost – it can interfere with the SSL of your other vhosts by trying to serve up their certificate in the wrong context. We avoid this by generating a self-signed wildcard certificate for use with this never-really-used default vhost.

$ sudo mkdir /etc/nginx/ssl
$ sudo openssl req -x509 -nodes -days 3650 -newkey rsa:2048 -keyout /etc/nginx/ssl/nginx.key -out /etc/nginx/ssl/nginx.crt

Answer the organisational detail prompts and be sure to enter an asterisk (*) in the Common Name field.

Edit the default vhost configuration at /etc/nginx/sites-available/default and add the following lines in the default server block:

listen 443 ssl default_server;
ssl_certificate /etc/nginx/ssl/nginx.crt;
ssl_certificate_key /etc/nginx/ssl/nginx.key;
ssl_protocols TLSv1.2 TLSv1.1 TLSv1;

In the same block, make sure the listen 80 directive also includes default_server, as above.

Test the config and restart Nginx:

$ sudo nginx -t
$ sudo service nginx restart

You should now be able to access your server on its IP or default hostname over HTTPS. You’ll be given a warning about the certificate, because it’s self-signed and they aren’t secure for this purpose, but don’t worry about that.

Lastly, generate a strong Diffie-Hellman profile as this will be used later in your SSL configuration:

$ sudo openssl dhparam -out /etc/ssl/nginx/dhparam.pem 2048

See the end of this guide for more info on using this self-signed cert for other vhosts.

Ensure your version of Nginx supports SNI: This is required for successful multi-host HTTPS. You’ll also need to be OK with denying access to any visitors using non-SNI capable browsers, which is mostly limited to Internet Explorer on Windows XP. Wikipedia has some further reading on this.

Check your Nginx version:

$ nginx -V

Underneath the version number it should say:

TLS SNI support enabled

Installing Let’s Encrypt’s Certbot: This is pretty simple:

$ cd /usr/local/sbin
$ sudo wget https://dl.eff.org/certbot-auto
$ sudo chmod a+x certbot-auto

Configuring your vhost to pass Certbot validation: In order to request certificates for your site, you have to prove your ownership by allowing Certbot to access a particular URL. To facilitate that, edit the Nginx config for your vhost and add the following lines to the server block:

location ~ /.well-known {
allow all;
root /home/youruser/sites/example.com/lets-encrypt-validation;
}

Adjust the path to match your site’s location on the server – I recommend creating a directory within your site files for these validation files to live in, or you can use a temporary location.

Test your Nginx config again and restart Nginx.

An important note here is that for each domain you wish to request a certificate for, Certbot will need to be able to access the above URL using that domain name. So make sure your Nginx configuration allows for this – for example if you want to request a certificate for example.com and staging.example.com, make sure that the above location block is processed by Nginx for requests to both domains. If the location isn’t accessible, Certbot will not validate your domain.

Requesting the certificate: Now we run the certificate request through certbot:

$ certbot-auto certonly -a webroot --webroot-path=/home/youruser/sites/example.com/lets-encrypt-validation -d example.com -d staging.example.com

Be sure to pass in the correct path to the validation directory as we defined above. Certbot creates files in that directory and then expects to be able to access them on the URL we defined in our Nginx config earlier. You can add as many additional domains as you like using the -d flag, but note that they will all be individually validated.

If all goes well…

Configuring Nginx to use the certificate: Add the following lines to your Nginx server block:

listen 443 ssl;
server_name example.com www.example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

Where example.com is your domain name. If you supplied multiple domains names to Certbot and aren’t sure which one is the authoritative one for this site, you can see what Certbot calls this site by listing the contents of /etc/letsencrypt/live/ .

Test your config again and restart Nginx. You should now be able to access your site over HTTPS and receive your new certificate in the handshake.

Stub HTTPS configs for other vhosts: If there are any vhosts on your server for which you do not intend to serve HTTPS traffic, you can add configuration to their Nginx configs to use the self-signed certificate we prepared above. That way, although HTTPS visitors will still get a certificate error, you can at least control the name and details of that certificate to make them appear friendlier. Otherwise, visitors may be served a certificate from one of your other HTTPS-enabled vhosts, which can cause confusion.

Setting up auto-renewal: To ensure your certificates stay up to date, add a task to root’s crontab:

$ sudo crontab -e

Add the following lines – this will cause Certbot to check for renewals once a week:

30 2 * * 1 /usr/local/sbin/certbot-auto renew >> /var/log/le-renew.log
35 2 * * 1 /etc/init.d/nginx reload

There’s more detail about this step in the link below. Hopefully, this is your HTTPS dance done!

Further reading: This is an intentionally brief guide aimed at coherently describing the main tasks in this process. I highly recommend reading more about the setup and maintenance of Let’s Encrypt (I used this guide originally) and adding much more to your Nginx configuration to (among other things) redirect from HTTP to HTTPS and bolster your SSL configuration to achieve an A rating or better on Qualys SSL Test. For that I used these two guides.