Setup HTTP/2 Connections in a Native Node.js Server
This article provides a straightforward guide on how to configure and
manage HTTP/2 connections using the native http2 module in
Node.js. You will learn how to initialize a secure HTTP/2 server, handle
incoming streams, and send multiplexed responses to the client without
relying on third-party frameworks.
Why Use Native HTTP/2?
HTTP/2 improves web performance through multiplexing (sending
multiple requests and responses concurrently over a single TCP
connection), header compression (HPACK), and server push capabilities.
Node.js features a built-in http2 module that allows you to
leverage these protocol advantages directly.
Because modern web browsers require TLS (encrypted connections) to
use HTTP/2, you must configure a secure server (HTTP/2 over TLS, or
h2).
Step 1: Generate SSL/TLS Certificates
To establish a secure connection, you need a private key and a self-signed certificate. You can generate these using OpenSSL in your terminal:
openssl req -x509 -newkey rsa:2048 -nodes -sha256 -keyout key.pem -out cert.pem -days 365Step 2: Create the HTTP/2 Server
Use the native Node.js http2 module to load your
certificates and create a secure server instance. Unlike HTTP/1.1, which
relies on standard request and response objects, HTTP/2 natively
operates on “streams.”
Create a file named server.js and add the following
code:
const http2 = require('node:http2');
const fs = require('node:fs');
const path = require('node:path');
// Read the SSL certificate and private key
const serverOptions = {
key: fs.readFileSync(path.join(__dirname, 'key.pem')),
cert: fs.readFileSync(path.join(__dirname, 'cert.pem')),
};
// Create the secure HTTP/2 server
const server = http2.createSecureServer(serverOptions);
// Log server errors
server.on('error', (err) => console.error('Server error:', err));
// Listen for incoming streams
server.on('stream', (stream, headers) => {
const requestPath = headers[':path'];
const requestMethod = headers[':method'];
console.log(`Received ${requestMethod} request for ${requestPath}`);
// Route handling
if (requestPath === '/' && requestMethod === 'GET') {
// Send headers using stream.respond()
stream.respond({
'content-type': 'text/html; charset=utf-8',
':status': 200,
});
// Write the body and close the stream
stream.end('<h1>Hello HTTP/2 from Native Node.js!</h1>');
} else if (requestPath === '/api/data' && requestMethod === 'GET') {
stream.respond({
'content-type': 'application/json',
':status': 200,
});
stream.end(JSON.stringify({ status: 'success', data: 'Multiplexing works!' }));
} else {
stream.respond({
':status': 404,
});
stream.end('Page Not Found');
}
});
// Start the server
const PORT = 8443;
server.listen(PORT, () => {
console.log(`HTTP/2 server running at https://localhost:${PORT}`);
});Step 3: Test Your HTTP/2 Server
Run your server using Node.js:
node server.jsTo verify that the server is communicating over HTTP/2, use the
curl tool in your command line with the
--http2 flag. The -k flag is necessary to
bypass SSL warnings if you are using a self-signed certificate:
curl -I -k --http2 https://localhost:8443/You should see :status: 200 and HTTP/2
indicated in the response headers.
Handling Client Disconnections
HTTP/2 streams can be aborted by the client at any time. It is
important to handle the close or error events
on individual streams to prevent your server from leaking resources:
server.on('stream', (stream, headers) => {
stream.on('error', (err) => {
console.error('Stream error:', err);
});
stream.on('close', () => {
console.log('Stream closed');
});
// Respond to the stream...
});