hazardous

thoughts, things, etc. from andrew snow

working with http/2

http/2: harder, better, faster, stronger

over the past month i have been working to optimize the site for the best possible first-load page rendering speed. among the various tips and suggestions provided by Lighthouse audits, one in particular recommended using the new http v2 protocol to handle browser-site handshakes. i won't waste too much time introducing http/2 and all its improvements here, but digitalocean provides a concise summary:

HTTP/2 is a new version of the Hypertext Transport Protocol, which is used on the Web to deliver pages from server to browser. HTTP/2 is the first major update of HTTP in almost two decades: HTTP1.1 was introduced to the public back in 1999 when webpages were usually just a single HTML file with inline CSS stylesheet. The Internet has dramatically changed since then, and now we are facing the limitations of HTTP 1.1 — the protocol limits potential transfer speeds for most modern websites because it downloads parts of a page in a queue (the previous part must download completely before the download of the next part begins), and an average modern webpage requires about 100 request to be downloaded (each request is a picture, js file, css file, etc).

HTTP/2 solves this problem because it brings a few fundamental changes:

  • All requests are downloaded in parallel, not in a queue
  • HTTP headers are compressed
  • Pages transfer as a binary, not as a text file, which is more efficient
  • Servers can “push” data even without the user’s request, which improves speed for users with high latency

for more, a brief-if-dense overview can be found here.

the benefits promised by this new protocol do not significantly allow for any noticeable improvements in loading times for my website - a small weblog powered by static content and a minimum of PHP. the realistic amount shaved off my overall times might be mere milliseconds. however, it's something of a quirk many developers share that the smallest and most inconsequential details can often consume the most of our attention, and so i devoted myself to getting up and running with this new protocol... primarily for the simple feat of being able to say i did it.

requirements

my own experiment utilized a traditional LEMP stack - NGINX and MariaDB+PHP, running from a Linux box. the only relevant thing here will be the choice of webserver - the following does not apply to servers powered by apache2, and will require looking elsewhere for notes on setting up http/2 to work with it.

since http/2 was designed around the use of SSL, you will need your own SSL certificates in order to utilize the HTTPS schema when connecting to your website. The easiest way to create and install your own SSL certs is to utilize the fantastic service provided by the folks at Let's Encrypt to generate certificates using certbot.

once you've got an NGINX server up and running with a website secured and accessible via HTTPS, we can now configure NGINX to handle all connections via http/2.

Step 1: server-wide setup

the first step is to navigate to your default nginx configurations directory, which is usually located in /etc/nginx on most Linux distros. open up /etc/nginx/nginx.conf and look for the following:

http {
...
    ##
    # SSL Settings
    ##

    ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # Dropping SSLv3, ref: POODLE
    ssl_prefer_server_ciphers on;
...

edit the above by simply deleting or commenting out TLSv1 and TLSv1.1, and save your file. depending on if your NGINX installation has been compiled with the relevant options, you can also add TLSv1.3.

for even more control over your webserver's handshakes, you can also specify which encryption schemes to accept. to do so, open /etc/nginx/nginx.conf and navigate to the section mentioned above, and add the following below the ssl_protocols and above the ssl_prefer_server_ciphers flags:

...
    ssl_ciphers EECDH+CHACHA20:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5;
...

once finished, save the file and check to make sure there are no routine issues by running sudo nginx -t. you should receive a message stating the configuring files are OK. common errors include typos, improperly nesting options, and duplicate options being called to. if you are using Certbot to manage your SSL certificates, you might need to comment out Certbot's own TLS configs in your server block configuration file:

    # include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot

Step 2: setting up your website connections

when finished with nginx.conf, head to your server block config which is usually located in /etc/nginx/sites-avaliable. find the config which is relevant to the website you're trying to change, and open it to find where your SSL port is defined. this will usually be port 443, defined with the listen option:

server {
    ...
    listen [::]:443 ssl; # catch-all option to reroute all server connections
    listen 443 ssl; 
    ...
...

to change, add the http2 option after the ssl option:

    listen [::]:443 ssl http2;
    listen 443 ssl http2;

once again, save your changes and then run sudo nginx -t to make sure everything is working as intended. if you get the OK, it's now time to reload NGINX by either rebooting your server or running sudo systemctl restart nginx.

voila! you should now have a webserver that will now handle all incoming connections with the http/2 protocol, which promises to streamline and improve the original HTTP protocol that has existed since the start of the world wide web. this legacy protocol continues to currently operate via a lengthy list of improvisations and workarounds, and the new v2 protocol's largest impact is that it has been built from the ground up with these workarounds built right in to be as efficient as possible when performing the traditional handshake between browser and server.