Let's Encrypt with nginx + auto renewal

Just a re-post of my old gist.

Let's Encrypt is a new, free, automated, and open Certificate Authority. Find out more out Let's Encrypt, and how you can use in the Let's Encrypt documentation.

This file will guide you through the minimum things you need to set up Let's Encrypt on your nginx webserver.

Before we get started we need to install the letsencrypt/certbot CLI tool. You can follow the installation instruction over at Certbot.

Setting up nginx

Temporary webroot / acme-challenge

Before we configure our vhosts we want to set up some default configurations. Start out creating a temporary webroot:

$ mkdir -p /usr/local/etc/nginx/letsencrypt

Next up we need to create this common router for our domains. This will point to the /.well-known/acme-challenge/ folder in our webroot to confirm the domains. Let's create the file /usr/local/etc/nginx/acmechallenge.conf:

# letsencrypt acme challenge for domain verification
location '/.well-known/acme-challenge/' {
  root /usr/local/etc/nginx/letsencrypt;

# redirect all traffic to http -> https
location / {
  return 301 https://$server_name$request_uri?;

nginx config

Create a confirugation file for your vhost/domain in the nginx.conf (or a sepparate file you include):

server {
  listen 80;
  listen [::]:80;
  server_name mydomain.com www.mydomain.com;

  include /usr/local/etc/ningx/acmechallenge.conf; # the config created in the step above

... then reload nginx:

$ service nginx reload

Now your server will redirect you to the https version every time you vists modomain.com.

Request certificate

This can be done in the CLI, but we're using a config file instead. Check out the documentation for more information on how to set up the configuration file.

Let's create mydomain.ini:

rsa-key-size = 4096
email = my@domain.com
domains = mydomain.com, www.mydomain.com
text = True
authenticator = webroot
webroot-path = /tmp/letsencrypt/

Run certbot to obtain the certificates:

$ certbot certonly -c mydomain.ini

(on some systems the tool would be named letsencrypt. I.e. FreeBSD).

Update the nginx config

Now we need to add a new server section to our vhost configuration who listen to port 443 (https):

server {
  listen 80;
  listen [::]:80;
  server_name mydomain.com www.mydomain.com;

  include /usr/local/etc/ningx/acmechallenge.conf;

server {
  listen 443;
  listen [::]:443;
  server_name mydomain.com, www.mydomain.com;

  ssl on;
  ssl_stapling on;
  ssl_stapling_verify on;

  ## These files are the certificates we just requested
  ssl_certificate           /usr/local/etc/letsencrypt/live/mydomain.com/fullchain.pem;
  ssl_certificate_key       /usr/local/etc/letsencrypt/live/mydomain.com/privkey.pem;
  ssl_trusted_certificate   /usr/local/etc/letsencrypt/live/mydomain.com/chain.pem;

  location / {
    root /usr/local/var/www/;

Reload nginx:

$ service nginx reload

and you're good to go!

Automatic renewal

Your Let's Encrypt certificates will expire after 3 month. It's recommended to renew them every 2 month. To do this we set up crontab to run the renew command every 2 month:

0 0 1 1,3,5,7,9,11 1 /path/to/certbot renew --quiet
5 0 1 1,3,5,7,9,11 1 service nginx reload

UPDATE: As Markus commented in the gist. It's an idea running the renew command more frequent. I.e. if you got multiple certificates from different time intervals, and in case of errors when it tries to renew. Then the cert will be outdated until next time the script runs.

Get even more secure!

To further increase security, you should generate a strong Diffie-Hellman group. This command will generate a 4096-bit group:

$ openssl dhparam -out /etc/ssl/certs/dhparam.pem 4096

To allow only the most secure SSL protocols and ciphers, and use the strong Diffie-Hellman group we generated, add the following lines to the server block:

add_header Strict-Transport-Security 'max-age=15552000; preload';
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;

gzip off;

ssl_dhparam /etc/ssl/certs/dhparam.pem; ## the diffie hellman group we generated

ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;

ssl_session_timeout 10m;
ssl_session_cache shared:SSL:10m;

ssl_stapling on;
ssl_stapling_verify on;


Compression over SSL may open up some bad stuff. gzip is therefore deactivated.


Deactivating TLSv1 and TLSv1.1 will give best security. But will leave out a lot of users whom still using older software.


See the nginx ssl documentation for https support.

The recommended cipher suite is*:


The recommended suite for backward compatibility(WinXP) is:


Run the command openssl ciphers to see the full list of ciphers.

You can simulate the handshake at SSL Labs.


Check out these posts for an overview on nginx' security:


Making mobile apps using Fuse