devnotes.

HTTP/2 in Node.js

HTTP/1.1 has served the web for decades, but it has a real bottleneck: a browser opens a limited number of connections per server, and each connection can only have one request in flight at a time waiting for a response before the next one starts. HTTP/2 was designed specifically to remove that bottleneck, and Node.js's http2 module lets you build a server that speaks it.

What HTTP/2 actually changes

  • Multiplexing — many requests and responses travel over a single connection at the same time, instead of queuing behind each other.
  • Header compression — repeated header data across requests gets compressed, cutting down overhead.
  • Server push — a server can proactively send resources it knows the client will need, before the client even asks.
  • Binary framing — data is transmitted in a more efficient binary format instead of HTTP/1.1's plain text.

A basic HTTP/2 server

HTTP/2 in practice almost always requires TLS (encryption), so a real server needs a certificate — but here's the shape of it:

const http2 = require('http2');
const fs = require('fs');

const server = http2.createSecureServer({
  key: fs.readFileSync('key.pem'),
  cert: fs.readFileSync('cert.pem')
});

server.on('stream', (stream, headers) => {
  console.log('Requested path:', headers[':path']);
  stream.respond({
    ':status': 200,
    'content-type': 'text/plain'
  });
  stream.end('Hello over HTTP/2');
});

server.listen(8443);

Notice 'stream' instead of 'request' — HTTP/2 works around the idea of independent streams multiplexed over one connection, rather than one request per connection.

Reading headers

HTTP/2 header names use a colon-prefixed pseudo-header convention for core request info:

server.on('stream', (stream, headers) => {
  console.log('Method:', headers[':method']);
  console.log('Path:', headers[':path']);
  console.log('Authority:', headers[':authority']); // roughly equivalent to the Host header
});

A quick client example

const http2 = require('http2');

const client = http2.connect('https://localhost:8443');
const req = client.request({ ':path': '/' });

let data = '';
req.on('data', (chunk) => { data += chunk; });
req.on('end', () => {
  console.log('Response:', data);
  client.close();
});
req.end();

Should you reach for this directly?

In practice, most Node.js applications don't build HTTP/2 servers by hand — they either sit behind a reverse proxy (Nginx, a CDN) that handles the HTTP/2 layer for them while the app itself speaks plain HTTP/1.1 internally, or use a framework with built-in HTTP/2 support. Knowing the module exists and roughly how it differs from http is useful context, but building directly on http2 is a deliberate, somewhat specialized choice rather than the default path.

HTTP/2 in Node.js | DevNotes