Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
How to setup Let's Encrypt for Nginx on Ubuntu 16.04 (including IPv6, HTTP/2 and A+ SLL rating)

How to setup Let's Encrypt for Nginx on Ubuntu 16.04 (including IPv6, HTTP/2 and A+ SLL rating)

There are two main modes to run the Let's Encrypt client (called Certbot):

  • Standalone: replaces the webserver to respond to ACME challenges
  • Webroot: needs your webserver to serve challenges from a known folder.

Webroot is better because it doesn't need to replace Nginx (to bind to port 80).

In the following, we're setting up mydomain.com. HTML is served from /var/www/mydomain, and challenges are served from /var/www/letsencrypt.


Nginx snippets

First we create two snippets (to avoid duplicating code in every virtual host configuration).

Create a file /etc/nginx/snippets/letsencrypt.conf containing:

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

Create a file /etc/nginx/snippets/ssl.conf containing:

ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
ssl_session_tickets off;

ssl_protocols TLSv1.2;
ssl_ciphers EECDH+AESGCM:EECDH+AES;
ssl_ecdh_curve secp384r1;
ssl_prefer_server_ciphers on;

ssl_stapling on;
ssl_stapling_verify on;

add_header Strict-Transport-Security "max-age=15768000; includeSubdomains; preload";
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;

Create the folder for the challenges:

sudo mkdir -p /var/www/letsencrypt/.well-known/acme-challenge

Nginx virtual hosts (HTTP-only)

We don't have a certificate yet at this point, so the domain will be served only as HTTP.

Create a file /etc/nginx/sites-available/mydomain.conf containing:

server {
	listen 80 default_server;
	listen [::]:80 default_server ipv6only=on;
	server_name mydomain.com www.mydomain.com;

	include /etc/nginx/snippets/letsencrypt.conf;

	root /var/www/mydomain;
	index index.html;
	location / {
		try_files $uri $uri/ =404;
	}
}

Enable the site:

rm /etc/nginx/sites-enabled/default
ln -s /etc/nginx/sites-available/mydomain.conf /etc/nginx/sites-enabled/mydomain.conf

And reload Nginx:

sudo systemctl reload nginx

Certbot

Install the package:

sudo apt-get install software-properties-common
sudo add-apt-repository ppa:certbot/certbot
sudo apt-get update
sudo apt-get install certbot

Note: there is also a letsencrypt package in APT, but it's a much older version of the client.


Get the certificate

Request the certificate (don't forget to replace with your own email address):

certbot certonly --webroot --agree-tos --no-eff-email --email YOUR@EMAIL.COM -w /var/www/letsencrypt -d www.domain.com -d domain.com

It will save the files in /etc/letsencrypt/live/www.mydomain.com/.

Note: The flag --no-eff-email opts out of signing up for the EFF mailing list, remove the flag if you'd like to signup.


Nginx virtual hosts (HTTPS-only)

Now that you have a certificate for the domain, switch to HTTPS by editing the file /etc/nginx/sites-available/mydomain.conf and replacing contents with:

## http://mydomain.com redirects to https://mydomain.com
server {
	listen 80;
	listen [::]:80;
	server_name mydomain.com;

	include /etc/nginx/snippets/letsencrypt.conf;

	location / {
		return 301 https://mydomain.com$request_uri;
	}
}

## http://www.mydomain.com redirects to https://www.mydomain.com
server {
	listen 80 default_server;
	listen [::]:80 default_server ipv6only=on;
	server_name www.mydomain.com;

	include /etc/nginx/snippets/letsencrypt.conf;

	location / {
		return 301 https://www.mydomain.com$request_uri;
	}
}

## https://mydomain.com redirects to https://www.mydomain.com
server {
	listen 443 ssl http2;
	listen [::]:443 ssl http2;
	server_name mydomain.com;

	ssl_certificate /etc/letsencrypt/live/www.mydomain.com/fullchain.pem;
	ssl_certificate_key /etc/letsencrypt/live/www.mydomain.com/privkey.pem;
	ssl_trusted_certificate /etc/letsencrypt/live/www.mydomain.com/fullchain.pem;
	include /etc/nginx/snippets/ssl.conf;

	location / {
		return 301 https://www.mydomain.com$request_uri;
	}
}

## Serves https://www.mydomain.com
server {
	server_name www.mydomain.com;
	listen 443 ssl http2 default_server;
	listen [::]:443 ssl http2 default_server ipv6only=on;

	ssl_certificate /etc/letsencrypt/live/www.mydomain.com/fullchain.pem;
	ssl_certificate_key /etc/letsencrypt/live/www.mydomain.com/privkey.pem;
	ssl_trusted_certificate /etc/letsencrypt/live/www.mydomain.com/fullchain.pem;
	include /etc/nginx/snippets/ssl.conf;

	root /var/www/mydomain;
	index index.html;
	location / {
		try_files $uri $uri/ =404;
	}
}

Then reload Nginx:

sudo systemctl reload nginx

Note that http://mydomain.com redirects to https://mydomain.com (which redirects to https://www.mydomain.com) because redirecting to https://www.mydomain.com directly would be incompatible with HSTS.


Automatic renewal using Cron

Certbot can renew all certificates that expire within 30 days, so let's make a cron for it. You can test it has the right config by launching a dry run:

certbot renew --dry-run

Create a file /root/letsencrypt.sh:

#!/bin/bash
systemctl reload nginx

# If you have other services that use the certificates:
# systemctl restart mosquitto

Make it executable:

chmod +x /root/letsencrypt.sh

Edit cron:

sudo crontab -e

And add the line:

20 3 * * * certbot renew --noninteractive --renew-hook /root/letsencrypt.sh

Conclusion

Congratulations, you should now be able to see your website at https://www.mydomain.com 🙂

You can now also test that your domain has A+ SLL rating:

I would also recommend setting up content-specific features like Content Security Policy and Subresource Integrity:

If Let's Encrypt is useful to you, consider donating to Let's Encrypt or donating to the EFF.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.