The Hypertext Transfer Protocol (HTTP) is the protocol that transmits hypertext around the Internet and in fact is what was used to deliver this blog to you on your browser. You likely don’t even think about it while checking your Facebook, but it’s really the foundation of the Internet. Version 1.1 is the most widely implemented version of this protocol, but did you know it’s over 20 years old? It could vote or buy alcohol, hopefully not at the same time, but that would explain a lot.
That’s a long time and that’s why version 2 (HTTP/2) was released a couple years ago. HTTP/2 makes many of the workarounds we had to implement to work around the limitations of HTTP/1 obsolete and even anti-patterns (looking at you, image sprites and domain sharding). There are many great improvements, but let’s talk about one that is very interesting and can have some real benefits to the performance of loading a website and that’s server push. What if you could have a 135% boost in load time performance using HTTP/2 server push over the old HTTP/1 way of loading and 12% boost with HTTP/2 without server push? Before you sign on the dotted line, let’s look at it first.
Server is the Boss!
Yes, server push. Instead of simply letting a browser load some HTML and determine what assets to download, we can push assets to the browser. Even though with HTTP/2 we don’t have to worry about how many sockets we are taking up with loading all those animated GIFs, there is still the process of the browser requesting those GIFs to form a round-trip request. But what if we could get rid of half of the request and just push the asset to the browser?
Not so Fast!
Great! So instead of having a request and a response, we just have a response! Yes and no. Browsers still cache assets and we’d hurt performance if we got rid of that caching. We need to know when the browser can load from the cache or needs to load from the server in order to know when we should push assets to the browser. Sounds complicated, doesn’t it? And it is but thankfully the folks at Google have thought of this and created a module that handles all this voodoo for us. All we have to do is implement a server able to serve via HTTP/2 and include this module, it’ll really be that simple!
Getting Fast
Fastify is an up-and-coming Node.js web framework designed to be… fast. It’s nearly as fast as using Node.js’ native HTTP module and almost twice as fast as Express. Fastify is also simple to configure, has lots of plugins and supports HTTP/2. A simple static server would be:
const fastify = require('fastify') const fastifyStatic = require('fastify-static') const fs = require('fs') const path = require('path') const STATIC_DIR = path.join(__dirname, 'static') const CERTS_DIR = path.join(__dirname, 'certs') const PORT = 3000 async function start () { const server = fastify({ http2: true, https: { key: fs.readFileSync(path.join(CERTS_DIR, 'key.pem')), cert: fs.readFileSync(path.join(CERTS_DIR, 'cert.pem')) } }) server.register(fastifyStatic, { root: STATIC_DIR }) await server.listen(PORT) console.log(`Listening on port ${PORT}`) } start().catch(console.error)
The one caveat to HTTP/2 is that browsers do require a SSL certificate. You can use the tls-keychain module to create a trusted certificate for local testing.
Load a sample HTML page with a couple assets and in the browser dev tools you can notice h2 is the protocol which means it was indeed loaded via HTTP/2:
Time to Push
Great, so we have a platform which we can use all the goodies of HTTP/2 but we aren’t pushing assets yet. Fastify has a plugin in order to use Google’s auto-push module called fastify-auth-push and it’s as simple as replacing the fastify-static module. Your server code would now look like:
const fastify = require('fastify') const { staticServe } = require('fastify-auto-push') const fs = require('fs') const path = require('path') const STATIC_DIR = path.join(__dirname, 'static') const CERTS_DIR = path.join(__dirname, 'certs') const PORT = 3000 async function start () { const server = fastify({ http2: true, https: { key: fs.readFileSync(path.join(CERTS_DIR, 'key.pem')), cert: fs.readFileSync(path.join(CERTS_DIR, 'cert.pem')) } }) server.register(staticServe, { root: STATIC_DIR }) await server.listen(PORT) console.log(`Listening on port ${PORT}`) } start().catch(console.error)
Only two lines were changed but looking at the dev tools when loading using this new server code, you can see the Initiator column has changed to show that the assets were pushed:
Conclusion
With very little changes, we implemented HTTP/2 server push, which is a cool buzzword! With a small test, the server push did cost us loading time on the HTML page but you can see the time it took to load the individual assets decreased even by 80%. Imagine a real website, you will undoubtedly have lots of assets, which would compound the overall savings. That’s where the Node.js Foundation has said they have seen about 12% improvement over delivering via HTTP/2 without server push. What kind of sites have you built where load times suffer because of waiting for assets to load, where pushing from the server could really help?
Mitchell Simoens
Related Posts
-
A JavaScript Developer's Take on Apple's Swift
This year Apple succeeded in making their 2014 WWDC one of the key talking points…
-
4 Key Concepts to Learning ReactJS
At Modus Create, our culture is defined by a few key objectives, including the value…