Easy HTTP/2 Server with Node.js and Express.js

Easy HTTP/2 Server with Node.js

The modern Internet with its TCP/IP protocol started around 1975 which is astonishing 41 years ago. For the most part of its existence, we used HTTP and it’s successor HTTP/1.1 (version 1.1) to communicate between clients and servers. It served the web well but the way developers build websites has dramatically changed. There are myriads of external resources, images, CSS files, JavaScript assets. The number of resources is only increasing.

HTTP2 is the first major upgrade to the good old HTTP protocol in over 15 years (first HTTP is circa 1991)! It is optimized for modern websites. The performance is better without complicated hacks like domain sharding (having multiple domains) or file concatenation (having one large file instead of many small ones).

H2 is the new standard for web which started as Google’s SPDY protocol. It’s already used by many popular websites and supported by most major browsers. For example, I went to Yahoo’s Flickr and it’s using h2 protocol (HTTP2) already (as of Jul, 2016).

Yahoo’s Flickr is using h2 protocol (HTTP2) already
Yahoo’s Flickr is using h2 protocol (HTTP2) already

HTTP/2 semantically is not any different from HTTP/1.1 meaning you have the same XML-like language in the body and also same header fields, status codes, cookies, methods, URLs, etc. The familiar to developers stuff is still there in H2. The benefits of H2 include:

  1. Multiplexing: Allows browsers to include multiple requests in a single TCP connection which in turn enables browsers to request all the assets in parallel.
  2. Server push: Servers can push web assets (CSS, JS, images) before a browser knows it needs them which speeds up page load times by reducing number of requests.
  3. Stream priority: Allows browsers to specify priority of assets. For example, browser can request HTML first to render it before any styles or JavaScript.
  4. Header compression: All HTTP/1.1 requests have to have headers which are typically duplicate the same info, while H2 forces all HTTP headers to be sent in a compressed format.
  5. De facto mandatory encryption: Although the encryption is not required, most major browsers implement H2 only over TLS (HTTPS).

While there’s some criticism of H2, it’s clearly a way forward for now (until we got something even better). Thus, let’s see what do you need to know as a web developer. Well, most of the optimization tricks you know become unnecessary, and some of them will even hurt website’s performance. I’m talking about file concatenation. Stop doing that (image sprites, bundled CSS and JS), because each small change in your big file will invalidate cache. It’s better to have many small files. I hope the need for build tools like Grunt, Gulp and Webpack will drop because of that. They introduce additional complexity, steep learning curve and dependencies to web projects.

Another thing which good developers did in the HTTP/1.1 world, and which will hurt you in H2 is sharding (trick to go over active TCP connection limit in browsers). Okay, it might not hurt in all cases, but there’s not benefits is since now of multiplexing. Don’t do domain sharding in HTTP2 because each domain incurs additional overhead. If you have to, then resolve domains to the same IP and make sure your certificates has a wildcard which makes it valid for the subdomains or have a multidomain cert.

For more info on HTTP/2, check out the official website. Now we’ll learn how we can create HTTP/2 server with Node.js Firstly, create an empty folder and a self-signed SSL certificate in it:

$ mkdir http2-express 
$ cd http2-express
$ openssl genrsa -des3 -passout pass:x -out server.pass.key 2048
...
$ openssl rsa -passin pass:x -in server.pass.key -out server.key
writing RSA key
$ rm server.pass.key
$ openssl req -new -key server.key -out server.csr
...
Country Name (2 letter code) [AU]:US
State or Province Name (full name) [Some-State]:California
...
A challenge password []:
...
$ openssl x509 -req -sha256 -days 365 -in server.csr -signkey server.key -out server.crt

When you’ll be visiting your server, make sure to select “ADVANCED” and “Proceed to localhost (unsafe)” or add localhost as an exception. The reason being is that browsers don’t trust self-signed certificates by default.

Click on ADVANCED
Click on ADVANCED

But it’s your cert so it’s okay to proceed.

Click on Proceed to localhost (unsafe)
Click on Proceed to localhost (unsafe)

Then, we need to initialize package.json and download spdy and express:

npm init
npm i express spdy --save

Now you can create index.js which will be an entry point of our application. It starts with some importations and instantiations:

const port = 3000
const spdy = require('spdy')
const express = require('express')
const path = require('path')
const fs = require('fs')

const app = express()

Next, we define an Express route:

app.get('*', (req, res) => {
    res
      .status(200)
      .json({message: 'ok'})
})

Then, we need to load the key and cert files. This is probably one of the few cases when we can use fs.readFileSync():

const options = {
    key: fs.readFileSync(__dirname + '/server.key'),
    cert:  fs.readFileSync(__dirname + '/server.crt')
}

Lastly, we load the SSL options into our server along with the Express instance:

spdy
  .createServer(options, app)
  .listen(port, (error) => {
    if (error) {
      console.error(error)
      return process.exit(1)
    } else {
      console.log('Listening on port: ' + port + '.')
    }
  })

When you start the server with node ., you can make CURL requests (make sure you got the latest version 7.46 with nghttp2) with curl https://localhost:3000/ -k to see the response (-k is to make CURL to be okay with self-signed certificate).

Using CURL with h2
Using CURL with h2

Another way to check that we’ve got H2, is to use DevTools like we did with Flickr in the beginning of this post.

Inspecting H2 in DevTools
Inspecting H2 in DevTools

That’s it. As you can observe, building an HTTP2 server with Node.js and Express.js is straightforward. In most cases, you won’t need to make many changes. Most likely, you’re already using HTTPS/SSL (and if you are not then you SHOULD unless your server is just for static assets). Then, you’d need to swap your https for spdy.

There’s also another library for H2 called http2, but it’s not working with the latest version of Express. You can use http2 without Express or just wait for Express v5.

In the end, HTTP/2 offers more benefits and removes the complexity of some web optimization tricks. Start reaping the reward of H2 now by implementing it in your servers. Onward to bright future!

PS: The source code for the working HTTP/2 Express.js server is in the github.com/azat-co/http2-express repository.

Author: Azat

Techies, entrepreneur, 20+ years in tech/IT/software/web development expert: NodeJS, JavaScript, MongoDB, Ruby on Rails, PHP, SQL, HTML, CSS. 500 Startups (batch Fall 2011) alumnus. http://azat.co http://github.com/azat-co

15 thoughts on “Easy HTTP/2 Server with Node.js and Express.js”

  1. Great post, however while following it, for some reason it doesn’t work for me. I have the following configuration

    Node Version – 6.11.2
    NPM Version – 3.10.10
    Express – 4.14.0
    Windows 10

    The error I get is express is not assignable to parameter of type ‘(request: IncomingMessage, response: ServerResponse) => void’

    The similar issue is posted in Github https://github.com/spdy-http2/node-spdy/issues/319

    Any help would be greatly appreciated.

    Thanks

  2. Thanks a lot! My server is running on http2. But what about redirection from http to https? How would you do that? I’m really strugling with it.

  3. H2 will load them in parallel so you should see a better load. Have you checked the Network tab? H1 will show as waterfall and H2 will have parallel requests.

  4. Nvm, looks like SPDY needs node v6.x. Upgrading my version fixed it. The reason I was dabbling with H2 was because I have ~350 requests made on initial page load (Angular 2 app) and I was hoping switching to H2 would speed that initial load up. However I am not seeing any speed up there. Am I wrong to expect a super fast initial load?

  5. I followed the steps you detailed and I see that my server is up but I see that Chrome is still using HTTP/1.1 as the protocol. Is there something I am missing?

  6. It would be nice if you put a timestamp on these articles… I have no idea how relevant it is without a date

  7. Thanks for the example. I would like to see further examples like how multiplexing works eg. sending multiple files down the pipe – if the request is for index.html also send app.css and app.js

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.