Properly Enable HTTPS on Nginx with Let’s Encrypt on Ubuntu

This tutorial shows you how to properly enable HTTPS on Nginx with Let’s Encrypt on Ubuntu. 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 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 score on SSL Labs test. If you follow my steps, you will get A+ score. 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.

Note: This tutorial works on all current versions of Nginx and Ubuntu (including 16.04, 18.04 and 20.04).

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 CA 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. IN CAA 0 issue ""

You can also use iodef to make CA report malicious certificate issue request to your email address. 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 CAA

Note that web browsers don’t check CAA records.

Security Headers

Security headers are as important as the HTTPS protocol, but only a small percentage of HTTPS-enabled sites pay attention to security headers. While a complete discussion about security headers is beyond the scope of this tutorial, I will talk about the upgrade-insecure-requests and HSTS headers, because you can easily enable them with Let’s Encrypt to increase your website 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 with an info icon; In Firefox, it’s replaced with 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 the upgrade-insecure-requests header, which will force web browsers 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 with your website should be done over 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. When a web browser connects to the site, Nginx can send the cached OCSP response 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 (Certbot) on Ubuntu

Now it’s time to get your hands dirty. Starting Ubuntu 16.04, Let’s Encrypt client (Certbot) is included in the Ubuntu repository, so you can install it with the following command. Python3-certbot-nginx is the Certbot Nginx plugin.

sudo apt install certbot python3-certbot-nginx

To check the version number, run

certbot --version

Sample output:

certbot 0.31.0

If you want to use the latest version, you can install Certbot from the Snap store.

sudo apt install snapd

sudo snap install --classic certbot

Note: If you want to use the Snap version, you need to use the full binary path: /snap/bin/certbot.

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, --email [email protected]


  • --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

  • /etc/letsencrypt/live/ The full chain of Root CA certificate, intermediate CA certificate, and your server certificate.
  • /etc/letsencrypt/live/ Your server’s private key

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 --uir flag. To manually add upgrade-insecure-requests header, edit your Nginx server block file.

sudo nano /etc/nginx/conf.d/

Add the following line in the SSL server block.

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

Also it’s a good idea to add a DNS resolver in the SSL server block.


If you don’t add a resolver, you might see the following message in Nginx error log.

[warn] 559#559: no resolver defined to resolve while requesting certificate status, responder:

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

sudo systemctl reload nginx

Troubleshooting Tip

If you see the following error messages when running certbot command, you simply need to run the certbot command again, since it’s a temporary failure in DNS resolution.

An unexpected error occurred:
Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/urllib3/", line 159, in _new_conn
    conn = connection.create_connection(
  File "/usr/lib/python3/dist-packages/urllib3/util/", line 61, in create_connection
    for res in socket.getaddrinfo(host, port, family, socket.SOCK_STREAM):
  File "/usr/lib/python3.8/", line 918, in getaddrinfo
    for res in _socket.getaddrinfo(host, port, family, type, proto, flags):
socket.gaierror: [Errno -3] Temporary failure in name resolution

Testing Your SSL Certificate

Go to 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/

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$request_uri;


return 301$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 contradict with Nginx configuration, your site will be in a redirect loop.

How to Enable HTTP/2 Protocol

To enable HTTP/2 protocol in Nginx, simply open your Nginx virtual host file and find the following line.

listen 443 ssl;

Add http2 at the end.

listen 443 ssl http2;

Save and close the file. Then reload Nginx.

sudo systemctl reload nginx

How to Disable TLSv1 and TLSv1.1

TLSv1 and TLSv1.1 are no longer considered secure. As of 2020-01-31, SSL Labs is capping servers that support TLS 1.0 or TLS 1.1 to B grade. To disable them, edit the Let’s Encrypt SSL options configuration file.

sudo nano /etc/letsencrypt/options-ssl-nginx.conf

Find the following line.

ssl_protocols TLSv1 TLSv1.1 TLSv1.2;

Remove the phased-out TLS versions.

ssl_protocols TLSv1.2

Save and close the file. Then restart Nginx.

sudo systemctl restart nginx

How to Enable TLS 1.3

Please read the following article:

How to Enable IPv6 in Nginx

If your server has IPv6 address and your domain name has AAAA record, then you should enable IPv6 in Nginx. Add the following line in the HTTP server block.

listen [::]:80;

And add the following line in the HTTPS server block.

listen [::]:443 ssl http2;

Save and close the file. Reload Nginx and you are done.

sudo systemctl reload nginx

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=[email protected]

Reloading Nginx is needed for it to 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


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.


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. IN CAA 0 issue "" IN CAA 0 issue "" IN CAA 0 issue "

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

If you use CloudFlare CDN and now you want to install Let’s Encrypt on your origin server, then run the following command to obtain and install Let’s Encrypt TLS certificate.

sudo certbot --nginx --agree-tos --redirect --uir --hsts --staple-ocsp --must-staple -d, --email [email protected]

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

Next Step

I hope this tutorial helped you enable HTTPS on Nginx with Let’s Encrypt on Ubuntu. You may also want to set up the ModSecurity web application firewall to protect your WordPress site from hacking.

Want to know how the server and web browser establish a secure HTTPS connection? Read the article below to learn HTTPS handshake.

As always, if you found this post useful, then subscribe to our free newsletter to get more tips and tricks. Take care 🙂

Rate this tutorial
[Total: 12 Average: 5]

14 Responses to “Properly Enable HTTPS on Nginx with Let’s Encrypt on Ubuntu

  • hernan lizarzaburu
    3 weeks 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)
      3 weeks ago

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

      • hernan lizarzaburu
        3 weeks 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.

      • Unable to get this working, I used the dig command to make sure my CAAs where correct, this is the never ending response:

        Client with the currently selected authenticator does not support any combination of challenges that will satisfy the CA. You may need to use an authenticator plugin that can do challenges over DNS.

        Kind regards

  • Thank you for this tutorial, Can you update it for ubuntu 18.04 and Latest version of Ngnix ?

    I have installed latest LEMP server on AWS EC2 instance, mostly followed your tutorial to get latest stack, all work fine, but installing and enabling https/ssl on latest nginx server is not easy. I read several tutorial and all of them tell different things to do.

    • Xiao Guo An (Admin)
      5 years ago

      This tutorial works on all current versions of Ubuntu and Nginx.

      • Thank you, I tried your tutorial and it worked.
        Thank you for all these great tutorials.

  • Hi Xiao. Thank you! This worked perfectly!

    After a lot of searching, this is the best and most comprehensive walkthrough I found. Thanks so much!

  • ejohnso49
    4 years ago

    Hi, great tutorial!

    I think there’s an error in the second part regarding redirects to www from non-www. In the example server block which captures HTTPS traffic sent to the non-www domain, it is missing some of the settings used in the server block after certbot finished. When I ran the test after following the section of the guide, I received a grade of B. However if I duplicated these into the server block, my test returned a grade of A+.

    This was the redirect from non-www to www https server block I ended up using:

    server {
        listen 443 ssl http2;
        listen [::]:443 ssl http2;
        ssl_certificate /etc/letsencrypt/live/; # managed by Certbot
        ssl_certificate_key /etc/letsencrypt/live/; # managed by Certbot
        include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
        ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
        add_header Strict-Transport-Security "max-age=31536000" always; # managed by Certbot
        add_header Content-Security-Policy upgrade-insecure-requests;
        ssl_trusted_certificate /etc/letsencrypt/live/; # managed by Certbot
        ssl_stapling on; # managed by Certbot
        ssl_stapling_verify on; # managed by Certbot;
        return 301$request_uri;
  • Hello Xiao,

    On ubuntu 20.04LTS when try to get the certificiates i get the following error:

    AttributeError: module ‘acme.challenges’ has no attribute ‘TLSSNI01’

    Can you help?

    • Xiao Guoan (Admin)
      4 years ago

      If you see the following error while trying to obtain TLS certificate on Ubuntu 20.04

      module 'acme.challenges' has no attribute 'TLSSNI01'

      You need to edit a config file.

      sudo nano /usr/lib/python3/dist-packages/certbot_nginx/


      return [challenges.HTTP01, challenges.TLSSNI01]


      return [challenges.HTTP01]

      Save and close the file. Then run the certbot command again to obtain TLS certificate.

      • Thank You

        I used yours but Certbot realesed a new installation files that resolved the issue:

  • Ednilso Nascimento
    3 years ago

    Hello thanks for your awesome posts.

    I used the instructions in this post to configure letsencrypt on my ubuntu 20.04 server with nginx.

    After the end of the process I receive the message:

    Unable to set enhancement ensure-http-header for
    Unsupported enhancement: ensure-http-header and not consigning to browse

    What is missing?

    Thanks for the help.

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.
  • Please use the community ( for questions unrelated to this article.
  • I don't have time to answer every question. Making a donation would incentivize me to spend more time answering questions.

The maximum upload file size: 2 MB. You can upload: image. Links to YouTube, Facebook, Twitter and other services inserted in the comment text will be automatically embedded. Drop file here