How To Enable TLS 1.3 in Nginx on Ubuntu 18.04 and 16.04

HTTPS just got faster and safer thanks to the release of TLS 1.3 by IETF (RFC 8446) in August 2018. TLS 1.3 is now the latest version of the TLS protocol. This tutorial will be showing you how to enable TLS 1.3 in Nginx web server on Ubuntu 18.04 and Ubuntu 16.04.

Update: There’s an easier way to enable TLS 1.3. See this article: How to easily enable TLS 1.3 in Nginx on Ubuntu 18.10, 18.04, 16.04, 14.04

TLS 1.3: Improved Performance and Security

Performance-wise, TLS 1.2 needs two round trips to establish HTTPS connection. With TLS 1.3, only one round trip is required. TLS 1.3 also supports zero round trip mode (0-RTT session resumption), allowing clients who have previously connected to your website to send HTTP request on the first message to the server. This makes a big difference for users on mobile networks or at far distant locations.

In terms of security, TLS 1.3 removed support for old cipher suites, which is responsible for exploits like ROBOT attack. As such server admins can no longer add old cipher suites in TLS 1.3 to appease users of old web browsers. This, of course, is an oversimplified explanation. CloudFlare has a good detailed explanation of TLS 1.3.

Enable TLS 1.3 in Nginx on Ubuntu 18.04, Ubuntu 16.04

There are two requirements when it comes to enable TLS 1.3 with Nginx.

  1. Your Nginx version must support TLS 1.3. That means nginx 1.13 or above.
  2. Nginx needs to either be built with OpenSSL 1.1.1+, or runs with OpenSSL 1.1.1+.

The second requirement may sound confusing, so let me give you two examples.

  • Ubuntu 18.04 ships with OpenSSL 1.1.0. Replacing the system OpenSSL library isn’t a good idea, but you can download OpenSSL 1.1.1 source code and compile Nginx with OpenSSL 1.1.1 source code to enable TLS 1.3.
  • Arch Linux currently ships with OpenSSL 1.1.1, but the Nginx package in Arch repository is actually built with OpenSSL 1.1.0. In this case, Nginx isn’t built with OpenSSL 1.1.1, but it runs with OpenSSL 1.1.1.

Now let’s see how to compile Nginx with OpenSSL 1.1.1 on Ubuntu 18.04 and Ubuntu 16.04.

1. Adding the Official Nginx Repository

Instead of downloading the source tarball and compiling it with make, I’m going to add the official Nginx repository to my Ubuntu server and then create a deb package from source code. In this way, I don’t need to manually add a long list of configuration parameter to the configure command. Also there will be a handy systemd service file within the deb package.

First fetch the Nginx GPG key and import it to Ubuntu.

wget http://nginx.org/keys/nginx_signing.key

sudo apt-key add nginx_signing.key

Then create a source list file for Nginx repo.

sudo nano /etc/apt/sources.list.d/nginx.list

Add the following two lines into the file. The deb-src line allows us to download Nginx source packages with apt source command. Bonic is the codename for Ubuntu 18.04. If you are using Ubuntu 16.04, replace it with xenial.  (Note: This repository doesn’t support 32 bit OS.)

deb [arch=amd64] http://nginx.org/packages/mainline/ubuntu/ bionic nginx
deb-src http://nginx.org/packages/mainline/ubuntu/ bionic nginx

To save a file in Nano text editor, press Ctrl+O, then press Enter to confirm. To exit, press Ctrl+X. Then update local package index.

sudo apt update

Now the Nginx official repository is added to Ubuntu server.

2. Download Nginx and OpenSSL Source Code

We will make a nginx directory under /usr/local/src/ to store the Nginx sources and then cd into that directory.

sudo mkdir /usr/local/src/nginx
 
cd /usr/local/src/nginx/

Download Nginx source package with the command below:

sudo apt install dpkg-dev

sudo apt source nginx

Check out the downloaded files.

ls

Output:

nginx-1.15.3                               nginx_1.15.3-1~bionic.dsc
nginx_1.15.3-1~bionic.debian.tar.xz        nginx_1.15.3.orig.tar.gz

Then clone the OpenSSL github repository.

cd /usr/local/src

sudo apt install git

sudo git clone https://github.com/openssl/openssl.git

cd openssl

Next, list all branches and switch to the 1.1.1 stable branch.

git branch -a

sudo git checkout OpenSSL_1_1_1-stable

3. Edit Nginx Compile Rules

Edit Nginx compile rules file.

sudo nano /usr/local/src/nginx/nginx-1.15.3/debian/rules

Find config.status.nginx: config.env.nginx section.  Add the following text at the end of CFLAGS line. Note that the following text isn’t a line by itself.

--with-openssl=/usr/local/src/openssl

ubuntu 18.04 nginx tls 1.3

Save and close the file.

4. Compile Nginx

Make sure you are in the Nginx source directory.

cd /usr/local/src/nginx/nginx-1.15.3/

Install dependencies to build our Nginx deb package.

sudo apt build-dep nginx

Now use the following command to build the deb package.

sudo dpkg-buildpackage -b

If you see the following error,

missing initializer for field 'md_ctrl' of 'EVP_MD {aka const struct evp_md_st}

Then edit the auto/cc/gcc file.

sudo nano /usr/local/src/nginx/nginx-1.15.3/auto/cc/gcc

Comment out the following line. The -Werror flag makes GCC treat warnings as errors.

CFLAGS="$CFLAGS -Werror"

Then rerun the build command. Once the build is complete, there will be a Nginx deb package in /usr/local/src/nginx/ directory. If you have installed Nginx before, it’s time to remove the old version and then install the new version.

sudo apt remove nginx nginx-common nginx-full

cd /usr/local/src/nginx/

sudo dpkg -i nginx_1.15.3-1~bionic_amd64.deb

Now let’s start Nginx.

sudo systemctl start nginx

If you see the following error message.

Failed to start nginx.service: Unit nginx.service is masked.

Then unmask nginx and issue the start command again.

sudo systemctl unmask nginx

Note that the Nginx process might run as user nginx or www-data. This can be changed by editing the first line in /etc/nginx/nginx.conf file. Just make sure Nginx run as the same user with PHP-FPM.

Now check the config arguments of Nginx.

sudo nginx -V

nginx tls 1.3 ubuntu

As you can see, we have the latest version of Nginx built with OpenSSL 1.1.1.

5. Enable TLS 1.3 in Nginx Server Block

Now I assume that you have already enabled HTTPS for your Nginx server block. The syntax to enable TLS 1.3 is fairly easy. Open your Nginx server block file in /etc/nginx/conf.d/ directory or /etc/nginx/sites-enabled/ directory. Find the following line.

ssl_protocols  TLSv1.2;

Add TLSv1.3 to the list of protocols.

ssl_protocols TLSv1.2 TLSv1.3;

Then add the following 3 cipher suites to your existing cipher suites.

TLS-CHACHA20-POLY1305-SHA256
TLS-AES-256-GCM-SHA384
TLS-AES-128-GCM-SHA256

like so:

ssl_ciphers 'TLS-CHACHA20-POLY1305-SHA256:TLS-AES-256-GCM-SHA384:TLS-AES-128-GCM-SHA256: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';

nginx tls 1.3 ubuntu 16.04

Save and close the file. Then test Nginx configuration and reload.

sudo nginx -t

sudo systemctl reload nginx

It’s unlikely that Ubuntu 18.04 would switch to OpenSSL 1.1.1, so if you upgrade Nginx with sudo apt upgrade command, TLS 1.3 will be gone. It’s a good idea to hold Nginx from being upgraded by apt with this command:

sudo apt-mark hold nginx

Update: As of Nginx 1.15.4, you can enable 0-RTT with OpenSSL by adding the following directive in the ssl server context. The default value is set to off.  In the previous version 1.15.3, it can only be used with BoringSSL.

ssl_early_data on

Enable TLS 1.3 in Google Chrome

Currently, Firefox 62 and Chrome 69 only support draft 28 of TLS 1.3. OpenSSL 1.1.1 supports the final version of TLS 1.3. Implementations based on draft version and the final RFC version do not interoperate with each other.

To test our Nginx server now, we need to install the beta version of Google Chrome and enable the final version of TLS 1.3. Once you have Chrome beta installed, Enter chrome://flags/#tls13-variant in the address bar and switch from default to Enabled (Final). Then relaunch Google Chrome for the change to take effect.

enable tls 1.3 chrome

Now visit your TLS 1.3 enabled website in Google Chrome beta and press Ctrl+Shift+I to open the developer tools page. Go to Security tab. You can see the version of TLS in use.

enable tls 1.3 nginx

Firefox is said to ship the final version of TLS 1.3 in Firefox 63, scheduled for October 2018. In Chrome 70, the final version of TLS 1.3 will be enabled for outgoing connections.

Update: Chrome 70 added support for the final version of TLS 1.3, but it by default is stilling using draft 28 of TLS 1.3.

Enable TLS 1.3 with CloudFlare

If you are using CloudFlare CDN (Content Delivery Network), then your website is already using TLS1.3. In CloudFlare dashboard, select the crypto tab and you will see the option to enable/disable TLS 1.3. You can also enable 0-RTT. CloudFlare supports both the draft 28 version and the final version.

tls 1.3 0-rtt

That’s it! I hope this tutorial helped you enable TLS 1.3 in Nginx on Ubuntu 18.04 and Ubuntu 16.04. Take care.

Rate this tutorial
[Total: 27 Average: 4.3]

25 Responses to “How To Enable TLS 1.3 in Nginx on Ubuntu 18.04 and 16.04

  • I compiled correctly and made the right configurations. I used Letsencrypt to generate the keys, yet I still end up with TLSv1.2 as the the highest supported protocol.

    nginx version: nginx/1.15.3
    built by gcc 7.3.0 (Ubuntu 7.3.0-16ubuntu3) 
    built with OpenSSL 1.1.1a-dev  xx XXX xxxx
    TLS SNI support enabled
    • Xiao Guo-An (Admin)
      3 months ago

      As explained in this article, the current stable version of Firefox and Chrome by default support the draft 28 of TLS 1.3. You can change Chrome settings to make it support the final version of TLS 1.3. In Firefox I have’t found such setting.

    • I have a site in Cloudflare with enabled TLSv1.3 and this same browser shows that it utilizes TLSv1.3. This site shows TLSv.1.3 as well.

      Doing SSL tests as well shows TLSv1.2 is used.

      server {
          listen 443 ssl http2;
          server_name site.se;
          ssl_certificate /etc/letsencrypt/live/site.se/fullchain.pem;
          ssl_certificate_key /etc/letsencrypt/live/site.se/privkey.pem;
          ssl_session_timeout 180m;
          ssl_prefer_server_ciphers on;
          ssl_ecdh_curve secp384r1;
          ssl_protocols TLSv1.2 TLSv1.3;
          ssl_ciphers 'TLS-CHACHA20-POLY1305-SHA256:TLS-AES-256-GCM-SHA384:TLS-AES-128-GCM-SHA256:ECDH+AESGCM:ECDH+AES256:ECDH+AES128:!ADH:!AECDH:!MD5';
          ssl_dhparam /etc/ssl/certs/dhparam.pem;
      
         location / {
             root /usr/share/nginx/tls13;
         }
      }
    • Are you sure your ciphers are correct, shouldn’t they include 13:

      TLS13-AES-

    • Xiao Guo-An (Admin)
      3 months ago

      Sorry, I forgot to tell you that you need to use Google Chrome beta and change the TLS 1.3 version. Chrome stable doesn’t include setting to switch to TLS 1.3 final version.

    • Xiao Guo-An (Admin)
      3 months ago

      Both

      TLS-CHACHA20-POLY1305-SHA256
      TLS-AES-256-GCM-SHA384
      TLS-AES-128-GCM-SHA256

      and

      TLS13-CHACHA20-POLY1305-SHA256
      TLS13-AES-256-GCM-SHA384
      TLS13-AES-128-GCM-SHA256
      

      are OK with Nginx.

    • It’s not about Chromes draft settings, both Beta and standard use the same 23 and 28, there is no final version in Beta.

      I tried to access the site I have on Cloudflare and it shows TLSv1.3 both on draft 23 and 28.

    • Chrome Beta on Mac doesn’t incorporate the Final draft, Windows does. I will test it on my Windows machine.

      But how come this site runs v1.3 even though I’m using standard Chrome (draft 28)? Is it compiled with an older version of OpenSSL?

    • Xiao Guo-An (Admin)
      3 months ago

      Because this site is on CloudFlare, which supports both draft 28 and final version.

    • If I already have nginx installed through a Linux package and sites running on it, should I remove the package after I have compiled nginx or prior? Or there is not difference and it won’t break my sites if I don’t purge the conf files.

    • Xiao Guo-An (Admin)
      3 months ago

      You can uninstall the previous Nginx package at any time before installing the new one. Your sites will be down for a few moment.
      Don’t purge your existing server block configuration files.

  • Carlos Eduardo
    3 months ago

    I also try this tutorial, my Nginx show built with OpenSSL 1.1.1a-dev, regenerate my site Keys, installed Chrome 70…but no TLS 1.3. Can’t understand whats happening.

    • Carlos Eduardo
      3 months ago

      Wow, i tested with Dev version and i get YES to TLS 1.3, if you want to test: https://dev.ssllabs.com/ssltest/

  • Thanks! YMMV but it looks like one does not even have to change cipher suites config after TLS1.3 is enabled – openssl enables them by default – see: https://wiki.openssl.org/index.php/TLS1.3#Ciphersuites
    I recompiled nginx with openssl 1.1.1 and everything started working after I enabled TLS1.3 protocol.

  • i faced problem after doing this step
    Comment out the following line. The -Werror flag makes GCC treat warnings as errors.

    CFLAGS=”$CFLAGS -Werror”

    the problem was clearsign failed: no secret key

    could you help me in solving the error

    • Xiao Guo-An (Admin)
      4 months ago

      Please post the nearby message as well.

    • dpkg-source — after-build nginx 1.15.6
      dpkg-buildpackage: binary-only upload(no source code included)
      signfile nginx_1.15.6-1~bionic_amd64.changes
      gpg: key ‘/home/sumaia/.gnupg/pubring.kbx’ created
      gpg: skipped “konstantin pavlov “: No secret key
      gpg: dpkg-sign.a_V4GIGK/nginx_1.15.6-1~bionic_amd64.changes: clearsign failed: No secret key

    • Xiao Guo-An (Admin)
      4 months ago

      This is normal. It just means the generated files can’t be signed because you don’t have a private key on this system. You can proceed to install the package.

    • i faced another problem when i run
      sudo dpkg -i nginx_1.15.3-1~bionic_amd64.deb

      it said me that:
      dpkg: error processing archive nginx_1.15.3-1~bionic_amd64.deb (–install):
      cannot access archive: No such file or directory
      Errors were encounterd while processing:
      nginx_1.15.3-1~bionic_amd64.deb
      can you help me

    • Xiao Guo-An (Admin)
      4 months ago

      Your Nginx version is now 1.15.6. Please replace 1.15.3 with 1.15.6.

  • thank you very much for your article it helped me to pass in TLS 1.3 and also to compile nginx with open ssl 1.1.1

  • Everything working as expected following guide for Ubuntu 16.04, thanks 🙂

  • Great tutorial!

    In case that someone encounters this error during build:

    dpkg-shlibdeps: error: no dependency information found for /usr/local/lib/libssl.so.1.1 (used by debian/nginx/usr/sbin/nginx-debug) Hint: check if the library actually comes from a package.
    

    You must modify this file:

    /usr/local/src/nginx/nginx-{VERSION_HERE}/debian/rules
    

    Look for this line:

    dh_shlibdeps -a
    

    And change it to add this flag

    dh_shlibdeps -a --dpkg-shlibdeps-params=--ignore-missing-info
    
  • Many thanks for “missing initializer for field ‘md_ctrl’ of ‘EVP_MD {aka const struct evp_md_st}”

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 (https://community.linuxbabe.com) 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