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 ;-)


  1. Interesting — I didn’t know about /.well-known/acme-challenge/. I guess I have dotfiles accessible :)

    Secondly, I though ssl_trusted_certificate is used for client certificate authentication. Are you actually using that?

    1. ssl_trusted_certificate is used for verifying client certificates and OCSP responses if ssl_stapling is enabled. To avoid a request from the client to the OCSP server to verify that the certificate is valid, you should use OCSP stapling on the web server.

  2. Before few days I read about cron renew via default lets encrypt service in cron via /home/user/.local/share/letsencrypt/bin/letsencrypt renew . External script for that is no long needed anymore.

    1. Yes, that will renew the certs, but it won’t reload Nginx, will it?

      I also usually need the script to do more, e.g. distribute the files to other nodes and reload Nginx on them as well.

  3. Hi Bjørn,

    iv been playing around a couple of months with my nginx on wheezy but there was always trouble to get the certificates.

    After find your Documentation, it works like a charm!

    THANK YOU so much!

    Best Regards,


  4. Thanks for this, I was specifically looking how to allow access to the .well-known/acme-challenge directory. Works like a charm :)

  5. Can you consider something like this in return:

    # /opt/letsencrypt/letsencrypt-auto renew –quiet –post-hook “service nginx reload” >> /var/log/le-renew.log
    2016-09-02 21:01:12,251:WARNING:certbot.cli:You are running with an old copy of letsencrypt-auto that does not receive updates, and is less reliable than more recent versions. We recommend upgrading to the latest certbot-auto script, or using native OS packages.

    and I found this: https://community.letsencrypt.org/t/message-about-out-of-date-software/16425

    Can you believe it? Should I just ignore this, or will certbot-auto prevail?

  6. Hi! Thanks so much for the tutorial..

    I’m getting :
    unknown directive “ssl_trusted_certificate” in /etc/nginx/sites-enabled/mysite

    Any ideas on what to do? Thanks!

  7. Hi Bjørn,

    Would it be possible to add a global alias for the .well-known/acme-challenge wwroot? It’s very hard to add that alias for every vhost when they are generated by different software components…

Comments are closed.