Enable HTTP/2 with Lets Encrypt SSL and Nginx on Ubuntu 16.04

In this tutorial, I am going to show you how to enable HTTP/2 with Lets Encrypt and Nginx on Ubuntu 16.04 to speed up and secure your NodeJS / ExpressJS web applications.

In the previous tutorials, I talked about how to install NodeJS and how to set up ExpressJS application with Nginx on Ubuntu. In this tutorial I will talk about how to add an extra features like HTTP/2 and SSL certificate to improve the speed and security of your application.

What is HTTP/2 ?

HTTP/2 is a major revision of the HTTP network protocol. With HTTP/2 enabled the web browser is allowed to make multiple concurrent requests to the web server using a single TCP connection which greatly reduces the bandwidth and server load. This make the applications run much faster than before and it is very important feature for those who want to see an additional boost in their SEO rankings. Most modern web browsers and web servers already support it and it is time to take advantage of it. To learn more about HTTP/2, I recommend you to read this HTTP/2 F.A.Q. There are some interesting answers on the most common questions and I am sure you will learn a lot of it.

Another good thing about HTTP/2 is that it cares about the security too. HTTP/2 requires TLS encryption to be implemented and TLS 1.2 or higher is a must have. This can easily be implemented with a free of chare SSL certificate issued from Lets Encrypt.

What is Lets Encrypt ?

Lets Encrypt is a certificate authority like Comodo or Symantec, but instead of charging you for an SSL certificate it issues free SSL certificates. The whole process of issuing, installing and renewing the SSL certificate is automated. The idea behind the Lets Encrypt project is to make encrypted connections ubiquitous.

Now that you know what HTTP/2 and Lets Encrypt are lets see how to use them for your applications.

Issue and install Lets Encrypt SSL certificate

There are many ways to issue an SSL certificate from Lets Encrypt and the recommended way it to use the Certbot ACME client. It allows you to automate the whole process including the installation of the SSL no matter if you are using Nginx or Apache as a web server. Also, it will automate the SSL reissuing process so you do not need to worry about the SSL expiration date.

However, I really don’t like autoconfigurations. I always want to configure the things by my own and according to my needs. So I will use a slightly different method to issue the SSL which includes manual installation as well as manual SSL reissue.

First, install git if it is not already installed on your machine:

sudo apt-get install git

Create an Nginx server block for your domain name:

sudo nano /etc/nginx/sites-available/example.com

Add the following content:

server {
        listen 80;
        server_name example.com www.example.com;
        root /var/www/html/example.com;
}

server {
    listen 443;
    server_name example.com www.example.com;
    root /var/www/html/example.com;
}

Note: Replace example.com with your actual domain name.

Create a symbolic link:

sudo ln -s /etc/nginx/sites-available/mariodimov.com /etc/nginx/sites-enabled/mariodimov.com

Create a directory which you can use as a webroot:

sudo mkdir /var/www/html/example.com

Check if there are errors:

sudo nginx -t

If everything is OK, restart the Nginx service:

sudo systemctl restart nginx.service

Then, download a local copy of Lets Encrypt to the /opt directory:

sudo git clone https://github.com/letsencrypt/letsencrypt /opt/letsencrypt

Navigate to the /opt/letsencrypt where Lets Encrypt is installed:

cd /opt/letsencrypt

Now, generate the SSL certificate using the following command:

sudo ./letsencrypt-auto certonly -d example.com -d www.example.com --webroot-path /var/www/html/example.com

If you are asked about how do you want to authenticate with the ACME CA, you can select the first option 1: Place files in webroot directory (webroot). It will take a couple of seconds for the SSL to be generated and you will see a message with the following content:

– Congratulations! Your certificate and chain have been saved at
/etc/letsencrypt/live/example.com/fullchain.pem.

During the process you will need to specify an administrative email address and accept the Terms of Service.

Edit the Nginx server block that you created earlier and add the following content:

upstream expressjs {
    server 127.0.0.1:3000;
}

server {
        listen 80;
        server_name example.com www.example.com;
        return 301 https://$host$request_uri;
}

server {
    listen 443;
    server_name example.com www.example.com;
    root /var/www/html/example.com;
    access_log  /var/log/nginx/expressjs.access.log;
    error_log   /var/log/nginx/expressjs.error.log;

    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
	ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:ECDHE-RSA-AES128-GCM-SHA256:AES256+EECDH:DHE-RSA-AES128-GCM-SHA256:AES256+EDH:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4";
    ssl_prefer_server_ciphers on;
    ssl_dhparam /etc/ssl/certs/dhparams.pem;

    proxy_buffers 16 64k;
    proxy_buffer_size 128k;

    location / {
        proxy_pass  http://expressjs;
        proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
        proxy_redirect off;

        proxy_set_header    Host            $host;
        proxy_set_header    X-Real-IP       $remote_addr;
        proxy_set_header    X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header    X-Forwarded-Proto https;
    }
}

Note: Replace example.com with your actual domain name.

Generate strong Diffie-Hellman (DH) parameter:

cd /etc/ssl/certs
sudo openssl dhparam -out dhparam.pem 4096

Check if there are errors with the configuration:

sudo nginx -t

If there are no errors you can safely restart the Nginx service:

sudo systemctl restart nginx.service

If your ExpressJS application is not started, you can start it now. Make sure that the application is listening on port 3000.
At this point you have Lets Encrypt SSL certificate installed for your domain name.

Enable HTTP/2 with Nginx

This is very simple. Edit the Nginx server block for your domain name and add http2 in the line that begins with listen 443. The line should look like the following one:

listen 443 ssl http2;

Save the file and restart the Nginx service one more time.

Check if HTTP/2 is enabled

There are many ways to check whether HTTP/2 is enabled and you can find some of them here

I personally use the KeyCDN HTTP/2 checker. Here is a screenshot of the output I get for my domain name:

Check the SSL certificate implementation

I personally use the SSL checker tool provided by ssllabs. It is a very detailed tool and will help you to troubleshoot any issues with your SSL. Since I enabled a strong cipher suite and generated strong DH parameter, the Overall Rating should be A.

One thing that is missing here is how to automatically renew the SSL certificate when its expiration date is nearing and I am planning to write a separate tutorial about this.

If you have any questions or thoughts you can post them in the comments below.

Related Posts