A simple Node.js module that creates a server supporting both HTTP/1.1 and HTTP/2 cleartext (h2c) with prior knowledge on the same port.
HTTP/1.1 has security limitations when used for connections from reverse proxies to upstream servers (http1mustdie.com). HTTP/2 offers significant improvements, but within secure networks and internal infrastructure, managing TLS certificates adds unnecessary complexity.
HTTP/2 Cleartext (h2c) provides some security benefits of HTTP/2 without requiring TLS (clear separation of request body and headers). However, migrating infrastructure from HTTP/1.1 to h2c requires a transition period where both protocols must be supported.
While Node.js's http2.createServer has an allowHTTP1 option, it only works with TLS connections. There are no plans to fix this limitation (nodejs/node#44887).
Hence this package.
srvh2ch11 solves this by:
- Creating a raw TCP server that listens for incoming connections
- Sniffing the initial bytes to detect the HTTP/2 connection preface (
PRI * HTTP/2.0) - Routing the connection to either an HTTP/1.1 or HTTP/2 server based on the detected protocol
Important: This module only supports HTTP/2 with prior knowledge. The client must know in advance that the server supports HTTP/2 cleartext and send the connection preface immediately. There is no HTTP/1.1 to HTTP/2 upgrade negotiation.
npm install srvh2ch11import srvh2ch11 from "srvh2ch11";
const server = srvh2ch11.createServer((req, res) => {
res.writeHead(200, { "Content-Type": "text/plain" });
res.end("Hello from HTTP/" + req.httpVersion);
});
server.listen(8080, () => {
console.log("Server listening on port 8080");
});import srvh2ch11 from "srvh2ch11";
const options = {
http1: {
// HTTP/1.1 specific options
},
http2: {
// HTTP/2 specific options
maxConcurrentStreams: 100
}
};
const server = srvh2ch11.createServer(options, (req, res) => {
res.writeHead(200, { "Content-Type": "text/plain" });
res.end("Hello from HTTP/" + req.httpVersion);
});
server.listen(8080);import srvh2ch11 from "srvh2ch11";
const options = {
forceH2c: process.env.SRV_FORCE_H2C === "true", // Only accept H2C connections in production
http2: {
maxConcurrentStreams: 100
}
};
const server = srvh2ch11.createServer(options, (req, res) => {
res.writeHead(200, { "Content-Type": "text/plain" });
res.end("Hello from HTTP/" + req.httpVersion);
});
server.listen(8080);Run with:
SRV_FORCE_H2C=true node server.jsCreates a server that can handle both HTTP/1.1 and HTTP/2 cleartext connections.
options(optional): Configuration object with the following structure:http1: Options passed tohttp.createServer()http2: Options passed tohttp2.createServer()forceH2c: Boolean (default:false) - Whentrue, forces HTTP/2-only mode, bypassing protocol detection and rejecting HTTP/1.1 connectionshttp11Compat: Boolean (default:false) - Whentrue, enables HTTP/1.1 compatibility transformations for HTTP/2 requests:- Maps
:authorityheader tohostheader - Filters out HTTP/1.1-specific headers (connection, keep-alive, etc.) to prevent warnings
- Maps
onRequestHandler: The request handler function with signature(req, res) => {}
Returns an object with:
listen(port, [callback]): Start listening on the specified portclose([callback]): Close all servers (raw, HTTP/1.1, and HTTP/2)on(event, handler): Add event listenersaddress(): Get the server addressh1Server: The underlying HTTP/1.1 server instanceh2Server: The underlying HTTP/2 server instancerawServer: The underlying raw TCP server instance
Test with HTTP/1.1:
curl http://localhost:8080/Test with HTTP/2:
curl --http2-prior-knowledge http://localhost:8080/Run the included example:
npm run exampleThe module includes comprehensive test suites:
# Run tests (automatically builds TypeScript first)
npm test# Run basic curl tests (automatically builds TypeScript first)
npm run test:bash# Test against Node.js 20, 22, and 24
npm run test:multiThe tests cover:
- HTTP/1.1 GET, POST, and concurrent requests
- HTTP/2 with prior knowledge (GET, POST, concurrent streams)
- Mixed protocol handling (HTTP/1.1 and HTTP/2 simultaneously)
- Server configuration with separate options
- Force HTTP/2-only mode (
forceH2coption) - HTTP/1.1 compatibility mode (
http11Compatoption) - Basic curl-based integration tests
- Compatibility across multiple Node.js versions (20, 22, 24)
- HTTP/2 module maturity: Node.js's
http2module is less mature thanhttp. For example, it doesn't offercloseAllConnections()andcloseIdleConnections()methods that are available in the HTTP/1.1 server. There may be other API gaps as well. - Prior knowledge only: This module only supports HTTP/2 with prior knowledge. There is no HTTP/1.1 to HTTP/2 upgrade negotiation (note that Upgrade negotiation has been deprecated by RFC 9113 anyway).
- Cleartext only: This module is designed for cleartext (non-TLS) connections. For TLS connections, use Node.js's native
http2.createSecureServer()withallowHTTP1: true.
Thanks to GitHub users @fkoemep and @stT-e5gna2z5MBS who shared example implementations in the Node.js issue #44887.
This package was made possible by Hypersequent, creators of QA Sphere - a fast, pleasant to use Test Management System.
MIT