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.