Now serving over http/2

22nd Jul 2019

http/2 is well and truly here, and is even partially supported by Internet Explorer 11. Check out the can I use page for http/2. It's time to stop bundling your JavaScript and Stylesheets into single files without consideration.

Web server implementation for http/2 is also good, with NGINX having supported it from it's core install for a few years now.

A big http/2 advantage is that you will not run into http 1.1's limits on the maximum number of parallel connections to a single domain. These vary between browser with Chrome by default supporting 6 connections (it's worth noting that each browser install can be manually configured to change this number, although I doubt many people do).

The http 1.1 way

Let's have a think about what happens when we request an imaginary webpage - edspencer.me.uk/test-page.html - over http 1.1.

Looking at a browser that supports a maximum of 6 connections at a time, imagine our test-page.html contains references to 9 external assets, referenced in the header, all hosted on the same domain.

  • edspencer.me.uk/stylesheet1.css
  • edspencer.me.uk/stylesheet2.css
  • edspencer.me.uk/stylesheet3.css
  • edspencer.me.uk/scripts1.js
  • edspencer.me.uk/scripts2.js
  • edspencer.me.uk/scripts3.js
  • edspencer.me.uk/image1.png
  • edspencer.me.uk/image2.png
  • edspencer.me.uk/font1.woff2

What is going to happen here? Well, assuming that our cache is empty, the first 6 referenced files will be downloaded first. Anything after the first 6 will be queued until one of the 6 download slots frees up. This will happen when a download completes.

A simplified analogy would be you're queuing at a checkout, and there are 6 tills staffed by 6 operators. A maximum of 6 people can be served at the same time.

This also happens for Ajax requests to the same domain, which must also form an orderly queue, with a maximum of 6 going over the wire at the same time.

There were a few workarounds for this in the http 1.1 world. One was to combine your assets into bundles. So in our above example, our 3 stylesheets become one single stylesheet, and our 3 JavaScript files would become a single .js file. This reduces the number of request slots needed by 4.

Another way would be to serve your assets from different domains in order to bypass the 6 connections per domain limit. For example, having your images served from images.edspencer.me.uk and your stylesheets from styles.edspencer.me.uk.

Both of these techniques worked well under http 1.1, but had their downsides.

Downside 1 - Serving styles and JavaScript for entire areas of a website that a client may never access

Imagine I have a whole section in my web application that is only for users with admin access. If I'm bundling all application styles and scripts into two respective files, I'm burdening the clients that will never access the admin tools of my application with the code for my admin tool. Their experience of my website would improve if I served them a smaller set of assets that did not include the code needed to run the admin tool that they will never access.

Downside 2 - Maintaining web infrastructure for serving from multiple subdomains

Setting up subdomains requires webserver and DNS configuration. I also then need to work out how I'm going to get the web application's static assets onto their relevant subdomains. It's a lot of effort.

The http/2 way

With http/2, you don't need to bundle anymore, and you can instead split and serve your web app using multiple files without worrying about blocking one of those limited download slots. This is largely because of the improvements in the protocol transport, which have resulted in the recommended minimum limit for browsers implementing http/2 being much greater, at 100 concurrent connections.

Splitting your bundles more logically, instead of bundling into one will result in more, smaller bundles being sent to the client. For example you could have a bundle that contains the JavaScript for the admin pages of a web app, and it'll only get served to the client should they land on an admin page.

If someone visits your web app and only lands on the homepage, you don't need to serve them with the code needed to run the admin pages of your web app.

Enabling http/2 on NGINX

Setting up http/2 on NGINX is trivial, with the only change needed being the inclusion of the characters "h2" into the listen definition for your site:

server {
listen 443 ssl http2;
...
}

All you then need to do is restart NGINX, and you're good to go. You can test this out by taking a peek at the network tab in the developer tools of your preferred browser. Note the "h2" in the Protocol column:

Screenshot of Google networking tools, showing h2 in the protocol column

The gains

This is a WordPress blog, and whilst I've taken steps to improve it's performance, I have struggled to get the raw number of assets needed by the site down. After enabling http/2, I got an immediate and significant performance score improvement from Google Lighthouse:

Good Google lighthouse performance results

I'll be migrating this blog to a headless CMS soon which will give me much more control and will hopefully give me a nice score of 100! Watch this space.

Notes

  • http/2 is only supported over SSL
  • AWS s3 does not support http/2. You will need to put Cloudfront in front of it in order to get http/2 support
  • Google Cloud CDN supports http/2 out of the box, without any additional services