How to Properly Enable HTTPS on Nginx with Let’s Encrypt on Ubuntu 16.04/17.10

This tutorial shows you how to properly enable HTTPS on Nginx with Let’s Encrypt on Ubuntu 16.04/17.10. Google Chrome and Firefox have already begun marking non-encrypted web pages with password input box as being insecure. Eventually all HTTP web pages will be marked as insecure. HTTPS will become default for any website. It’s also a requirement if you want to utilize the new HTTP/2 protocol to speed up your website.

Let’s Encrypt is a free, automated, and open certificate authority. The official documentation describes simple steps you can follow to enable HTTPS with Let’s Encrypt, but there’s more to it than that. If you follow the official doc, you get an A on SSL Labs test. If you follow my steps, you will get an A+. If you’ve already deployed a Let’s Encrypt certificate before, you can still follow this tutorial to renew and replace your existing certificate.

This tutorial is divided into 3 parts.

  1. The first part is about CAA record, security headers and OCSP stapling. These things are what can help you get A+.
  2. The second part is about redirect www domain to non-www domain and vice versa.
  3. I will show you how to deal with CloudFlare CDN service in the third part.

Creating CAA Record for Your Domain Name

Certificate Authority Authorization (CAA) is a DNS resource record that specifies which certificate authorities (CAs) are allowed to issue certificate for a particular domain name. Starting September 2017, All CAs are mandated to check CAA records before issuing certificate for a particular domain name. If no CAA record is found for a domain name, then any CAs can issue certificate for that domain name. If a CA is not listed in your CAA record, then that CA cannot issue certificate for your domain name.

To create a CAA record which allows Let’s Encrypt to issue certificate for your domain name, add the following entry in your DNS server or DNS manager.

example.com. IN CAA 0 issue "letsencrypt.org"

You can also use iodef to make CA report malicious certificate issue request to your email address.

example.com. IN CAA 0 iodef "mailto:your-email-address"

The format of the above records is for zone files. Below are a few tips for you.

You can use the following dig command to check your CAA record.

dig example.com CAA

Note that web browsers does not check CAA records.

Security Headers

Security headers are as important as HTTPS protocol, but only a small percentage of HTTPS-enabled sites pay attention to those headers. While a complete discussion about security headers is beyond the scope of this tutorial, I want to talk about the upgrade-insecure-requests and HSTS headers, because you can easily enable them with Let’s Encrypt to increase your site’s security.

Upgrade Insecure Requests

There are times when a site has enabled HTTPS, but some CSS, images or JavaScripts are still served over HTTP. In this case, the green padlock at the beginning of browser address bar will disappear. In Google Chrome, it’s replaced by an info icon; In Firefox, it’s replaced by a gray padlock with a yellow triangle. You will want to show a green padlock to site visitors as often as possible and the easy way to fix this problem is to enable upgrade-insecure-requests header, which will force the web browser to use https:// for every http:// resource.

To enable this header, simply add --uir flag when issuing certbot command. Note that this header works on resources hosted on your own domain and resources on third-party domains that support HTTPS. If your web page includes resources on third-party servers that aren’t available over HTTPS, then those resources will be blocked by web browsers, but using this header ensures that your web pages always get a green padlock.

HSTS (HTTP Strict Transport Security)

The HSTS header tells web browsers that all communication should be done via HTTPS. It defends against SSL Striping, which is an attack to downgrade from HTTPS to HTTP. To enable this header, simply add --hsts flag when issuing certbot command.

OCSP Stapling

When a web browser connects to a HTTPS website, it sends an OCSP (Online Certificate Status Protocol) request to the certificate authority (CA) in order to query the revocation status of the website’s SSL certificate. This can delay page loading by 1-3 seconds, according to Firefox telemetry data. To improve performance, website owner can enable OCSP stapling, in which case the web server itself fetches OCSP response signed by CA at regular interval and sends it to web browser, thus eliminating the need for web browser to contact OCSP server.

To enable OCSP stapling, simply add --staple-ocsp flag when issuing certbot command.

OCSP Must Staple

If a hacker make a fake, duplicate website, turn off OCSP staple and also block the web browser’s access to OCSP server, then the web browser will assume it’s OK and proceed to the malicious website. To solve this problem, you can enable OCSP must staple on your website, which tells web browsers that OCSP staple response must be presented by your website during HTTPS connection. So when web browsers connect to a fake website that doesn’t have OCSP staple, it will stop the connection.

To enable OCSP must staple, add --must-staple flag when issuing certbot command.

Installing Let’s Encrypt Client on Ubuntu 16.04/17.10

It’s time to get your hands dirty. Starting Ubuntu 16.04, Let’s Encrypt client is included in Ubuntu repository. My suggestion is that you install it from the official Certbot PPA to get the latest version. Run the following commands. software-properties-common is required if you want to install packages from PPA. It’s often missing on a default Ubuntu server Install. python-certbot-nginx is the Certbot Nginx plugin.

sudo apt install software-properties-common

sudo add-apt-repository ppa:certbot/certbot

sudo apt update

sudo apt install certbot python-certbot-nginx

To check version number, run

certbot --version

Sample output:

certbot 0.19.0

Using Certbot Nginx Plugin to Enable HTTPS

If your website doesn’t use CDN service, then it’s recommended to use the Nginx plugin to enable HTTPS on Nginx web server, as it can automatically obtain SSL/TLS certificate and configure it for you. Run the following command on your Ubuntu server.

sudo certbot --nginx --agree-tos --redirect --uir --hsts --staple-ocsp --must-staple -d www.example.com,example.com --email your-email-address

Explanation:

  • –nginx: Use the Nginx authenticator and installer
  • –agree-tos: Agree to Let’s Encrypt terms of service
  • –redirect: Add 301 redirect.
  • –uir: Add the “Content-Security-Policy: upgrade-insecure-requests” header to every HTTP response.
  • –hsts: Add the Strict-Transport-Security header to every HTTP response.
  • –staple-ocsp: Enables OCSP Stapling.
  • –must-staple: Adds the OCSP Must Staple extension to the certificate.
  • -d flag is followed by a list of domain names, separated by comma. You can add up to 100 domain names.
  • –email: Email used for registration and recovery contact.

You will be asked if you want to receive emails from EFF(Electronic Frontier Foundation). After choosing Y or N, your SSL certificate will be automatically obtained and configured for you, which is indicated by the message below.

ubuntu letsencrypt nginx

Now if you visit your website, you can see that HTTP is automatically redirected to HTTPS connection. It turns out that the Nginx plugin currently doesn’t support --hsts and --uir flag. To manually add HSTS and upgrade-insecure-requests header, edit your Nginx server block.

sudo nano /etc/nginx/conf.d/example.com.conf

Add the following two lines in the server block.

add_header Strict-Transport-Security "max-age=15768000; preload" always;

add_header Content-Security-Policy upgrade-insecure-requests;

Save and close the file. Then reload Nginx for the changes to take effect.

sudo systemctl reload nginx

Testing Your SSL Certificate

Go to ssllabs.com to test your SSL certificate and configuration. As I’ve promised, you get A+. You can also check if your domain name has enabled CAA record, whether your server has enabled HSTS, OCSP stapling and OCSP must staple.

ubuntu certbot nginx

Redirecting WWW to Non-WWW (Or Vice-Versa)

We have already enabled redirecting HTTP to HTTPS, what’s left to do is redirect www to non-www, or vice versa. If you are using WordPress, then it’s very easy. Simply go to WordPress Dashboard > Settings > General and set your preferred version (www or non-www) in WordPress Address and Site Address.

letsencrypt nginx redirect

If you go that route, you will end up with what’s known as double 301 redirect. First, Nginx server redirect HTTP to HTTPS, then WordPress redirects to www or non-www domain. Some may argue that double 301 redirect can hurt your site’s SEO. If you are worried about that, then you can use the method below to make all domain versions to go directly to the final destination.

Edit your Nginx server block.

sudo nano /etc/nginx/conf.d/example.com.conf

CertBot client added the following lines to the file to redirect HTTP to HTTPS.

if ($scheme != "https") {
        return 301 https://$host$request_uri;
    } # managed by Certbot

You can delete those 3 lines and edit your server block configurations like the screenshot below to redirect non-www to www domain.

  • The first server block listens on port 80.  It contains a 301 redirect to redirect HTTP to HTTPS.
  • The second server block listens on port 443. It contains a 301 redirect to redirect non-www to www domain.

lets encrypt nginx redirec non-www to www

If you want to redirect www to non-www domain, then change

return 301 https://www.example.com$request_uri;

to

return 301 https://example.com$request_uri;

And change the server_name directive in the SSL server blocks.

Save and close the file. Test Nginx configurations.

sudo nginx -t

If the test is successful, reload Nginx  for the changes to take effect.

sudo systemctl reload nginx

If you are using WordPress, make sure you set your preferred domain version in WoredPress Address and Site Address before editing Nginx server block configuration file. If WordPress settings contradicts with Nginx configuration, your site will be in a redirect loop.

Certificate Auto Renewal

To automatically renew Let’s Encrypt certificate, simply edit root user’s crontab file.

sudo crontab -e

Then add the following line at the bottom.

@daily certbot renew --quiet && systemctl reload nginx

--quiet flag will suppress standard output. If you want to receive standard error, then add the following line at the beginning of crontab file.

MAILTO=your-email-address

Reloading Nginx is needed for it for present the new certificate to clients.

Setting PATH in Crontab

Sometimes Cron sends me the following message, which can be also seen in /var/log/letsencrypt/letsencrypt.log file.

Could not choose appropriate plugin for updaters: The nginx plugin is not working; there may be problems with your existing configuration.
The error was: NoInstallationError()

The cause of this error is that by default the PATH in Cron is set to

PATH=/usr/bin:/bin

But the nginx binary is located at /usr/sbin/nginx, Cron cannot find it with the default PATH. To fix this error, add the following line at the beginning of Crontab file.

PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin

CloudFlare CDN

If you want to install Let’s Encrypt certificate on your server and at the same time use CloudFlare’s CDN service, then you will need to enable CloudFlare’s Universal SSL on your site, which means

  • Connections between site visitors and CloudFlare edge server are encrypted using CloudFlare Universal SSL certificate
  • Connections between your origin server and CloudFlare edge server are encrypted using Let’s Encrypt issued certificate.

If you install Let’s Encrypt certificate on your origin server and redirect HTTP to HTTPS, but turn off CloudFlare Universal SSL, Web browsers will complain that your website is in a infinite redirect loop because CloudFlare redirect HTTPS to HTTP when Universal SSL is not enabled.

The second thing you need to know is that if you want to enable CAA record while using CloudFlare Universal SSL, then you also need to create the following CAA record.

example.com. IN CAA 0 issue "comodoca.com"

example.com. IN CAA 0 issue "digicert.com"

example.com. IN CAA 0 issue "globalsign.com

Follow this post to add CAA record for CloudFlare Universal SSL certificate.

So how do you go about installing Let’s Encrypt certificate with CloudFlare? Well, there are two scenarios.

  1. You’ve already installed Let’s Encrypt certificate using the above steps, now you want to enable CloudFlare CDN service.
  2. Your website is using CloudFlare CDN service, now you want to install Let’s Encrypt certificate on your origin server.

The First Scenario

If you are in the first scenario, then you can go ahead and enable CloudFlare CDN service and also enable CloudFlare Universal SSL in CloudFlare Dashboard by going to Crypto > SSL and choosing Full (Strict). Your site will be working fine without a problem.

The Second Scenario

The Certbot Nginx plugin uses Nginx as both the authenticator and installer. The Nginx authenticator uses the tls-sni-01 challenge to prove domain control. That means Certbot needs to set up a temporary TLS server listening on port 443. It won’t work if your site is behind a CDN like CloudFlare as Let’s Encrypt authenticate server can’t see your origin IP, thus can’t reach the temporary TLS server on your origin server.

If you use CloudFlare CDN and now you want to install Let’s Encrypt on your origin server, then you will need to use the webroot authenticator to obtain certificate and use Nginx installer to automatically configure SSL/TLS. Specify authenticator with --authenticator flag and the installer with --installer flag like below. /var/www/html/ is the path to web root. Change it accordingly.

sudo certbot --authenticator webroot --installer nginx -w /var/www/html/ --agree-tos --redirect --uir --hsts --staple-ocsp --must-staple -d www.example.com,example.com --email your-email-address

After the certificate is obtained and installed on your server, remember to enable CloudFlare Universal SSL.

I hope this tutorial helped you enable HTTPS on Nginx with Let’s Encrypt on Ubuntu 16.04/17.10. As always, if you found this post useful, then subscribe to our free newsletter to get more tips and tricks.

Rate this tutorial
[Total: 10 Average: 4.6]

3 Responses to “How to Properly Enable HTTPS on Nginx with Let’s Encrypt on Ubuntu 16.04/17.10

  • hernan lizarzaburu
    1 month ago

    Hi, you have awesome tutorials first of all, but I followed this one and I can not work it out, I have goddady domain and I changed every records but it does not work still appear FIX ERROR. Do you know how can I fix it up?. Thank you.

    • Xiao Guo-An (Admin)
      1 month ago

      Hi, please tell me in which step you encountered an error. Also post the error message.

    • hernan lizarzaburu
      1 month ago

      Hi, thank you for your reply, but I found the error it was in VM with repositories. Any way thank you a lot and your tutorials are the best.

Leave a Comment

  • Comments with links are moderated by admin before published.
  • Your email address will not be published.
  • Use <pre> ... </pre> HTML tag to quote the output from your terminal/console.
  • * Some of my previous answers are lost after I uninstalled Disqus comment system from my website. I try to recover those answers whenever I can.