Modern Website over HTTP 2 and SSL with Nginx and Let's Encrypt

Triangles. Generated through trianglify

The free and open certificate authority, Let’s Encrypt has been out in public beta for the last few months and v0.3.0 of their client was recently released, so I decided to give it a try on one of my side projects:

Additionally, Nginx, the web server behind, received an update in September that supports HTTP2 requests. While HTTP2 does hold the promise of increased performance, most likely won’t see the benefit because most the assets are pulled from CDNs like jsDelivr, but it will be a good future proofing excersise.

Step 0: Testing Along the Way

We’ll be testing our server externally through the use of cURL. We’ll see the upgrade, the switch to https, and http 2 represented in the response. The following command will let us see.

curl -sI "" | egrep -i "Server|HTTP"

cURL, as packaged, is unable to query using HTTP 2, so you’ll have to build it from source by first building nghttp2 and then building cURL by configuring with the --with-nghttp2 flag and path. After everything is built, you’ll see the previous command returning HTTP 2 headers.

Step 1: Updating Nginx

We need a recent version of Nginx, but default package managers are likely to carry only outdated versions, so hop over to the official linux distrubtion page. The stable package doesn’t include the HTTP2 module (--with-http_v2_module), so use the mainline version.

Below we add the Nginx key to apt, add their repo, and update.

curl -L "" | sudo apt-key add -
DEB_CMD="deb $(lsb_release -c -s) nginx"
sudo bash -c 'echo "${DEB_CMD}" >> /etc/apt/sources.list'
sudo apt-get update

We can test the Nginx server and HTTP version with apt-cache showpkg nginx. Newer versions of Nginx should appear. Let’s install the most recent one.

sudo apt-get install nginx
sudo service nginx restart
nginx -v

If the previous snippet does not work, it’s because the nginx maintainer for ubuntu somewhat emulates the apache server configuration, so you may have to go a few more steps.

sudo apt-get remove nginx-common

# Will ask you to overwrite previous configuration, say no or else you'll need
# to change the new nginx configuration to emulate apache
sudo apt-get install nginx
sudo service nginx restart
nginx -v

The latest and greatest Nginx is installed, but because you can’t enable HTTP 2.0 unencrypted (if you can, you shouldn’t because browsers may reject it) we have to get our certificate. Next step!

Step 2: Let’s Encrypt: Retrieving Certificates

Getting and setting up Let’s Encrypt on a machine is somewhat of a controversial topic. As a system administrator, you are putting a lot of trust into this program because:

  • Requires root privileges. You’re giving Let’s Encrypt the power to do anything on your machine, including mess it up
  • Heavy installation. Installs various dependencies including GCC, which is the not what I want to be installing on what should be a slim server with limited disk space.
  • Is a black box. You execute a command and all your certificates including your key are generated. If you’re not careful, it’ll rewrite your Apache configuration as well!

Naturally I tried looking for alternatives that alleviated all mentioned problems:

After several hours I gave up and went back to the Let’s Encrypt docs and found this golden nugget:

Unless you have a very specific requirements, we kindly ask you to use the letsencrypt-auto method. It’s the fastest, the most thoroughly tested and the most reliable way of getting our software and the free SSL certificates!

They really should have focused on making the client as small and self contained as possible and then build upon that foundation. Not spend time prioritizing an ncurses GUI or auto-magically updating your site’s configuration. All the while, closing sane requests to open up the black box as “too difficult”. Ok, rant done. It’s beta software and getting better every day.

Once you pull down letsencrypt-auto here are the commands to run to get handed the cert from already running site:

sudo ./letsencrypt-auto certonly --webroot -w <nginx_website_root> \
                        --email <email_for_registration> -d <domain_name>

# Chown so that the nginx user can read it!
sudo chown -R www-data.www-data /etc/letsencrypt

You’ll see your Let’s Encrypt certificate along with the chain, full cert, and private key.

Step 3: Installing Certificates

SSL configuration of a server is probably less intuitive than probably one thinks. To help us, Mozilla created an SSL configuration generator and I would recommend starting with the intermediate Nginx configuration. Since we’ll want http 2 and Let’s Encrypt auto renewal, modify the pull request to add http2 and include the following snippets:

listen [::]:443 ssl http2;

# TLS certificate renewal
location ~ .well-known {
  allow all;

Step 4: Auto Renewal

The Let’s Encrypt official client will have a renew command in 0.4.0 , so I won’t spend too much time on this because this section will be outdated shortly. In the meantime, I recommend using the le-renew-script