Set up Let’s Encrypt with Nginx web server with webroot plugin

26 January, 20173 min readWeb Development

This is one (of many) methods to speed up creating free SSL certificates with Let's Encrypt.

It configures the Nginx web server to serve /.well-known/acme-challenge/ for each domain. This path is used by the webroot plugin.

We’ll need to make a directory to service the challenge files from, we’ll call this /home/www/letsencrypt from now on, and we’ll need to make sure this is set up with suitable permissions so Nginx can serve these files to the public.

In Nginx’s config, we can add the following to each server block configuration:

location ^~ /.well-known/acme-challenge/ {
    default_type "text/plain";
    root /home/www/letsencrypt;
}

Generally, adding this in /etc/nginx/global/global.conf and including this file (usually already done by a default install) in each *.conf file in /etc/nginx/conf.d/ is recommended.

What this will do is each time a request is made with a URL such as:

http://yourdomain.com/.well-known/acme-challenge/something.file

Nginx will always attempt to respond with the file at

/home/www/letsencrypt/something.file

Install certbot-auto

wget https://dl.eff.org/certbot-auto
chmod a+x certbot-auto
mv certbot-auto /usr/local/bin

Add a certificate for a domain

certbot-auto certonly --webroot -w /home/www/letsencrypt -d domain.com

During this process, Let's Encrypt's servers will make a request to http://dommain.com/well-known/acme-challenge/* and it expect the same content as what certbot-auto has written to the /home/www/letsencrypt directory.

You need to make sure certbot-auto has write permissions to the direction given with the -w parameter.

Configure nginx server blocks

In each domain’s server block add:

listen               443 ssl;
ssl_certificate      /etc/letsencrypt/live/domain.com/fullchain.pem;
ssl_certificate_key  /etc/letsencrypt/live/domain.com/privkey.pem;
include              ssl/ssl.conf;

In ssl/ssl.conf we have:

ssl_session_timeout       5m;
ssl_protocols             TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers               "ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!MD5:!DSS";
ssl_prefer_server_ciphers on;
ssl_session_cache         shared:SSL:10m;
ssl_dhparam               /etc/nginx/ssl/dhparams.pem;

The above SSL config is my personal preference, but you can adjust to your liking.

If you don't have a DH parameters file configured, you can generate one with

openssl dhparam -out /etc/nginx/ssl/dhparams.pem 2048

Renew all certs if near expiry

For domains which have perviously had a certificate issued, you can renew them with

certbot-auto renew --webroot -w /home/www/letsencrypt

This can be added as a cron job.

00 2 * * * root /usr/local/bin/certbot-auto renew --webroot -w /home/www/letsencrypt 2> /dev/null

If you wish the automation to apply for a single domain only, use:

00 2 * * * root /usr/local/bin/certbot-auto renew --webroot -w /home/www/letsencrypt --cert-name NAME 2> /dev/null

Note that this will renew the certificates, but your nginx web server will not know they have renewed. You need to reload nginx's config files to make sure new certificates have been read. You could make a cron job to do this, some short time after the renewal attempt:

15 2 * * * root nginx -s reload 2> /dev/null

Certificate names are usually the same as the domain name, however this may not always be the case. You can check your certificate names with:

certbot-auto certificates

To test configuration

certbot-auto renew --webroot -w /home/www/letsencrypt --dry-run
© Andy Gock 2009−2019