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…