Configuring nginx to use SSL certificates from Let’s Encrypt using the
Webroot method
isn’t hard, but
there are a few steps to make it all work. We assume:
- you have only one website setup in nginx and that any additional sites will
also use Let’s Encrypt for their SSL certificates.
- HTML is served from
/var/www/example.com
Prerequisites
- Ubuntu 16.04
- nginx
- You must own or control the registered domain name that you wish to use the
certificate with.
- A DNS A Record that points your domain to the public IP address of your
server. Let’s Encrypt requires this to validate that you own the domain.
You will need an A Record for both
www.example.com
and example.com
if you want to support both domains.
Step 1 - Installing Certbot
The easiest way to request a SSL certificate from Let’s Encrypt is to use
the certbot
. The Certbot developers have their own [Ubuntu] software
repository with update-to-date versions of the software.
First add the repository:
sudo apt-get install software-properties-common
sudo add-apt-repository ppa:certbot/certbot
sudo apt-get update
You’ll need to press ENTER
to accept the certbot
repository.
Now install Certbot:
sudo apt-get install certbot
The certbot
Let’s Encrypt client is now ready.
Step 2 - Create Nginx Configs
Let’s Encrypt uses a challenge system to ensure the domain for which the SSL
certificate is about to be issued is controlled by the requester. To avoid
duplicating the logic in every virtual host configuration (as we’re setting up
both example.com
and www.example.com
) we’ll create a snippet.
sudo mkdir -p /etc/nginx/snippets/
Create /etc/nginx/snippets/letsencrypt.conf
containing:
location ^~ /.well-known/acme-challenge/ {
default_type "text/plain";
root /var/www/letsencrypt;
}
Create the ACME challenge folder (as referenced above):
sudo mkdir -p /var/www/letsencrypt/.well-known/acme-challenge
We need to create the server specific dhparam.pem
file. This will take
a while:
sudo apt-get install openssl
sudo openssl dhparam -out /etc/ssl/certs/dhparam.pem 2048
Now, within /etc/nginx/conf.d
we’ll create the global SSL settings. Create
/etc/nginx/conf.d/ssl.conf
containing:
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
ssl_session_tickets off;
ssl_protocols TLSv1.2;
# Use the "Modern" cipher suite recommended by Mozilla
# https://wiki.mozilla.org/Security/Server_Side_TLS#Modern_compatibility
ssl_ciphers
'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256';
ssl_ecdh_curve secp384r1;
ssl_prefer_server_ciphers on;
ssl_dhparam /etc/ssl/certs/dhparam.pem;
ssl_stapling on;
ssl_stapling_verify on;
# Enable HTST to ensure communication is only over SSL
add_header Strict-Transport-Security "max-age=15768000; includeSubdomains;
preload";
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
Step 3 - Configure Domain to Respond to ACME Challenge
As we don’t have any SSL certificates yet, we need to configure our domain to
respond to the ACME challenges. Assuming your nginx virtual server
configuration is at /etc/nginx/sites-available/example.com.conf
add:
include /etc/nginx/snippets/letsencrypt.conf;
between your server { ... }
blocks.
Enable the site:
sudo rm /etc/nginx/sites-enabled/default
sudo ln -s /etc/nginx/sites-available/example.com.conf /etc/nginx/sites-enabled/example.com.conf
And then reload nginx:
sudo systemctl reload nginx
Step 4 - Obtaining an SSL 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.example.com -d example.com
It will save the files into /etc/letsencrypt/live/www.example.com/
Note: The --no-eff-flag
opts out of signing up for the EFF mailing
list.
Step 5 - Setup HTTPS-Only Virtual Hosts
Now that we have the SSL certificates, switch your domain to HTTPS. Edit
/etc/nginx/sites-available/domain.com.conf
and replace the HTTP server
configs with:
## http://example.com redirects to https://example.com
server {
listen 80;
listen [::]:80;
server_name example.com;
include /etc/nginx/snippets/letsencrypt.conf;
location / {
return 301 https://example.com$request_uri;
}
}
## http://www.example.com redirects to https://www.example.com
server {
listen 80 default_server;
listen [::]:80 default_server ipv6only=on;
server_name www.example.com;
include /etc/nginx/snippets/letsencrypt.conf;
location / {
return 301 https://www.example.com$request_uri;
}
}
## https://example.com redirects to https://www.example.com
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name example.com;
ssl_certificate /etc/letsencrypt/live/www.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/www.example.com/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/www.example.com/fullchain.pem;
location / {
return 301 https://www.example.com$request_uri;
}
}
## Serves https://www.example.com
server {
server_name www.example.com;
listen 443 ssl default_server;
listen [::]:443 ssl default_server ipv6only=on;
ssl_certificate /etc/letsencrypt/live/www.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/www.example.com/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/www.example.com/fullchain.pem;
# The rest of your previous HTTP virtual server configuration. Below is an
# example:
root /var/www/example.com;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
Finally, reload nginx:
sudo systemctl reload nginx
Note that http://example.com
redirects to https://example.com
(which
redirects to https://www.example.com
) because redirecting to
https://www.example.com
directly would be incompatible with HSTS.
Step 6 - Automatic Renewal using Cron
Certbot can renew all certificates that expire within 30 days. We’ll create
a cronjob that automatically updates the certificates (and restarts nginx to
pick up the changes.)
Let’s try a dry-run to ensure that everything is working:
sudo certbot renew --dry-run
Create /usr/local/sbin/letsencrypt.sh
containing:
#!/bin/bash
systemctl reload nginx
# If you have other services that use the certificates add reloads here:
Make it executable:
sudo chmod +x /usr/local/sbin/letsencrypt.sh
Edit the root’s cron:
sudo crontab -e
And add:
20 3 * * * certbot renew --noninteractive --renew-hook /usr/local/sbin/letsencrypt.sh
Conclusion
And that’s it! If all went well you should see your site at
https://www.example.com/
!