My experience with Lets Encrypt and nginx

Let's EncryptA couple years back, I started slowly transitioning my sites to SSL. It was a nightmare.

Basic steps back then were:

  1. Shopping around – they can be pricey.
  2. Converting all my sites that had multiple subdomains (www, cdn, download, etc) to just www and non-www versions, since all DV certificates only gave you non-www and a single subdomain (typically used for www).
  3. Validation – sometimes a call, sometimes messing with domain emails, etc.
  4. Generating certificate requests.
  5. Combining certs – they have to be combined in specifics orders and depend on your web server.
  6. Getting them on the server.
  7. Finding out they weren’t combined right. Or that the server is misconfigured. Or both. Troubleshooting.

Needless to say, after the 1-year certificates expired, I ponied up the money for 5-year certs. No need to go through those headaches every year.

Fast forward to today, and I noticed that “Let’s Encrypt” was out of beta. I take care of a couple smaller sites that never ended up with SSL certificates, so I figured I’d give it a try.

First, the “gist” of how things work with Let’s Encrypt:

  • Free SSL certificates. Standard DV, but I’d call it “DV+” for reasons I’ll go into later.
  • Very flexible when it comes to multiple domains or multiple subdomains.
  • Currently come with a 90 day expiry, but renewal is free. The intent/design seems to be oriented towards automating renewal so that you set everything up once and your server takes care of grabbing new certificates automatically once they’re 30 days from expiry.
  • A number of methods available for obtaining certificates. Web-based, various clients, a program on your server (allowing easy renewal automation), etc.
  • The certificates are provided in ready-to-use format. No need to combine, etc.
  • In some cases, you just run a program on your server and it takes care of all the set-up. Create a cron-job to run a check periodically and it’ll update your certificate well before it expires.

Big benefits over the standard providers:

  • Perpetually free.
  • Much easier to install.
  • Much easier/faster to update the certificate when it expires (can be automated).
  • A single cert can handle up to 100 domain entries (,,,,, etc). Part of the reason I call it “DV+” is because with normal providers a “DV” cert only gives 2 (the domain + 1 subdomain), so you either have to spend big $ or buy a whole bunch of DV certificates. Here you get way more, and it’s free!

Some smaller downsides are that there are a few limits in place. That 100 domain entires I talked about above… well, if you’re trying to use them ALL on subdomains for a single domain, you’ll run into a 20-per-week limit. If you’re doing a bunch of testing and try to re-issue a certificate you generated (for the same entries), it won’t let you do so more than 5 times in a week. Fortunately, neither are things that are likely to be an issue for the average user.

Okay, on to my actual experience setting it up with nginx.

LetsEncrypt/CertBot Installation

A lot of available clients are listed, but I used the standard recommended “certbot“.

It’s worth noting that Ubuntu 16.04’s repo used the older “letsencrypt” client which didn’t work with some hooks I needed to use (to reload nginx automatically when the cert is updated), so I downloaded and used “certbot-auto” instead.

Generating the Certificate

Normal instructions can be found right on the CertBot site after you list your web server and distro.

The nginx side of development didn’t look like it was quite as polished as the apache side yet though, so I opted for a slightly more manual method, based on dumping what I needed into the command line. Instructions for using the “webroot” method can be found at , but essentially I ran the following:

./certbot-auto certonly --webroot -w /var/www/ -d -d --staple-ocsp -m

A few little notes here:

  • It temporarily places a verification file on each of the “-d” website locations, under a “/.well-known/” directory. Not a problem except that I was redirecting everything to non-www. I thus had to edit my config so it wouldn’t redirect accesses to stuff inside “” but would keep redirecting the rest.
  • You can put multiple domains in this request. So -w -d -d can become -w -d -d -w -d -w -d -d -d and so forth. The pattern is pretty straightforward.
  • –staple-ocsp wasn’t originally in there, but afterwards, the site failed an OCSP stapling test (not that they don’t fail sometimes anyway), so I added it and re-ran. You may-or-may-not want to use it. However, because you can only regenerate the same certificate 5 times per week, this is an area where you’ll probably want to look through all the configuration options, decide what all you want, and try to get it right the first time!

A few .pem files were generated, and you’ll see them plugged into nginx next.

Installing the Certificate with nginx

This part is where things were far less tricky because I already had a bunch of SSL stuff set up and could just copy/paste/replace in my nginx site configuration. Basically, using the default location, the main stuff to plug into nginx within the server block was:

ssl_trusted_certificate /etc/letsencrypt/live/;
ssl_certificate /etc/letsencrypt/live/;
ssl_certificate_key /etc/letsencrypt/live/;

…if you’re copying this, you DO need other stuff set up to enable SSL (there are a lot of guides out there if you’re starting fresh), but for this part, replace the bit with the proper path.

Automating Certificate Renewal

Now the fun part! Fun because it’s a little hard to test (due to the limit of 5 cert reissue’s per week), and it won’t update your cert automatically for 60 days so you won’t know for sure if it’s all hunky-dory for a couple months.

But here we go…!

Okay, so we want 2 things to happen when a certificate is renewed:

  1. Certificate renews.
  2. Nginx is reloaded so that it grabs the new certificate. Also if there’s a config issue, o we ran out of memory, or some other unfortunate issue that prevents nginx from doing a “reload”, we’ll get an email.

To handle #2, you can create a bash script that looks like this:

( service nginx testconfig && service nginx reload ) || echo "NGINX failed a testconfig or restart after updating certificates!" | mail -s "NGINX failure on"

…we’re assuming that your server WILL send email that WON’T end up in your spam folder. That’s another chore if you’re on a fresh server and haven’t tweaked mail settings or set up SPF records yet.

Anyway, what the above does is run testconfig, just in case something broke with nginx during the last 60 days. If all is good it reloads nginx. If all is STILL good, it’s done.

However, if either testconfig or reload failed, it sends an email.

You *could* probably add a “restart” as the fallback or restart the server, but we’re dice-rollers here.

Now to #1. Renewing the certificate in a way that triggers the above. A fairly straightforward command:

/path/to/certbot-auto renew -m --staple-ocsp --agree-tos --post-hook '/path/to/'

…edit to be correct for you (certbot location, email, your script location, and other options). You can add that to a daily/weekly cron.

You can run it to make sure it works – since your cert is brand new and hasn’t expired yet, it shouldn’t replace it and thus shouldn’t use up any of your 5/week cert re-issues unless you add –force-renew.

Could be a good idea to email yourself the output when the cron runs. If you try something custom to email only when it actually replaces the certificate, note that I didn’t have any luck with $RENEWED_DOMAINS or $RENEWED_LINEAGE in the renew hook, and ended up with a bash script that just saved all the output to a variable and set it up to email the output to me if it contained the text “following certs have been renewed“.

Final Thoughts

Despite things being a little more tricky to set up in my case (some being self-inflicted), having a free automated system to handle SSL moving forward is a big plus. No more deadlines to watch for, no more worrying about having too many subdomains for a DV, one less expense, and a good bit more flexibility.

The limits on grabbing new certs during initial testing and issues with hooks (and variables that didn’t work) were a bit of a downer.

Both of those were pretty minor and short-lived though. I suspect once my other certificates expire I’ll be moving everything to Let’s Encrypt.