Let’s Encrypt for Nginx

Let’s install an SSL-certificate from Let’s Encrypt for Nginx.

An official plugin for Let’s Encrypt for Nginx does exist, but “nginx support is experimental, buggy, and not installed by default” (not my words, it’s from ./letsencrypt-auto --help).

So until stable support for Nginx is available, we’ll use Let’s Encrypt to provide us with the certificates and install them manually. After the initial installation, we’ll make sure the renewal process is fully automated.

I assume you have knowledge on how to secure Nginx with HTTPS and also know the info in Optimizing HTTPS on Nginx.

Install Certbot
– the Let’s Encrypt client

The official Let’s Encrypt client is now called Certbot. We’ll use git to get the client, so make sure it is installed. On Ubuntu/Debian you’ll use apt:

$ sudo apt-get install git

Clone the Certbot client repo to a somewhat sane location. If you use a different path than mine, please remember that you did so and replace the paths later accordingly.

$ sudo git clone https://github.com/certbot/certbot /opt/letsencrypt

Prepare Nginx

The Let’s Encrypt verification server will look for verification files created by the client in a subdir of your Nginx webroot: /.well-known/acme-challenge/

This means that these files must be publicly accessible. If you deny access to all files and dirs that start with a dot, you may add this to your Nginx config:

# Allow access to the letsencrypt ACME Challenge
location ~ /\.well-known\/acme-challenge {
    allow all;

Enable/disable logging as you like. Remember to reload Nginx.

Get the certificate

Make sure you specify the correct webroot and that all domains you provide actually points there. The verification server will look for the autogenerated files for letsencrypt in /.well-known/acme-challenge/*

$ sudo /opt/letsencrypt/certbot-auto certonly --agree-tos --webroot -w /path/to/public/www/ -d example.com -d www.example.com

If everything goes well (read the output) you should have the following files:

Your letsencrypt private key:


Your letsencrypt certificate:


The letsencrypt intermediate certificates:


Your certificate and intermediate certificates concatenated in the correct order:


They’re actually symlinks to the most recent version you have, so when you renew or replace a letsencrypt certificate or key, you don’t have to worry about updating paths. The letsencrypt client Certbot will automatically update the symlinks. Don’t move the files elsewhere.

Now is a good time to backup the entire /etc/letsencrypt directory to a really secure location. In addition to the certificate, this dir also contains your Let’s Encrypt account key.

Update the Nginx config

You may now point to the files from Let’s Encrypt in your Nginx config:

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

When you reload Nginx, you should now have certificates from Let’s Encrypt. Now we just have to …

Automate the letsencrypt renewal process

The certificates from Let’s Encrypt are only valid for 90 days. Even though Let’s Encrypt will send you an email if you’re approaching the expiration date, you don’t want the hassle of manually renewing the certificates all the time.

Previously, we had to use our own script to check if the certificate was up for renewal and reload Nginx if necessary. With recent versions of Certbot, we just need a crontab entry like this to renew our letsencrypt certificate:

43 5 * * 1 /opt/letsencrypt/certbot-auto renew --quiet --post-hook "/usr/sbin/service nginx reload" >> /var/log/le-renew.log

This runs the renewal once per week (you can run it every day if you want to), and will renew all certificates that are up for renewal. If at least one certificate was renewed, the command in the --post-hook argument will run. If you need to run more than an Nginx reload, I recommend you put it in an external script and use the path to it in the --post-hook. If no certificates were renewed, the argument will be ignored.


There are many ways to achieve the same result. If you have a better way of doing something, please leave a comment. I would also appreciate if you explain WHY your method is better – other than it being your way of course ;-)