Go back Setting Up and Testing an HTTP/2 Server with Node.js /* by Tirth Bodawala - July 27, 2024 */ Tech Update In the previous blog, we covered setting up an HTTPS 1.1 server using Node.js. If you haven’t gone through it yet, you can find it here. In this blog, we will take a step further and set up an HTTP/2 server, which offers significant performance improvements over HTTP 1.1. Prerequisites Node.js (version 20.04 or higher) Basic understanding of JavaScript and Node.js SSL certificates (refer to the previous blog for generating them) Step 1: Create the HTTP/2 Server We’ll create a new server file for our HTTP/2 server. Add a file http2.js inside the src/server directory with the following content: import { createSecureServer } from 'node:http2'; import { createReadStream } from 'node:fs'; import { readFileSync } from 'node:fs'; import { extname, resolve } from 'node:path'; import mime from 'mime-types'; import zlib from 'node:zlib'; const http2Port = 8081; const http2Host = '0.0.0.0'; const brotliOptions = { chunkSize: 32 * 1024, params: { [zlib.constants.BROTLI_PARAM_QUALITY]: 10, }, }; // SSL options const options = { key: readFileSync(resolve('ssl', 'server-key.pem')), cert: readFileSync(resolve('ssl', 'server-cert.pem')), allowHTTP1: true }; const html = readFileSync(resolve('src', 'index.html'), { encoding: 'utf-8' }); // Create the HTTP/2 server with TLS const http2Server = createSecureServer(options, async (req, res) => { const filePath = resolve(req.url.substring(1)); if (req.url === '/') { res.writeHead(200, { 'Content-Type': 'text/html', }); res.write(html); res.end(); return; } try { const mimeType = mime.lookup(extname(filePath)) || 'application/octet-stream'; res.writeHead(200, { 'Content-Type': mimeType, 'Content-Encoding': 'br' }); const readStream = createReadStream(filePath); const brotliStream = zlib.createBrotliCompress(brotliOptions); readStream.pipe(brotliStream).pipe(res); } catch (error) { console.log(error); res.writeHead(404, { 'Content-Type': 'text/plain' }); res.write('File not found'); res.end(); } }); http2Server.listen(http2Port, http2Host, () => { console.log(`HTTP/2 server listening on: https://${http2Host}:${http2Port}`); }); Explanation of the HTTP/2 or h2 Server Code 1. Import Required Modules: We import necessary modules such as createSecureServer from node:http2, createReadStream, readFileSync from node:fs, extname, resolve from node:path, and mime from mime-types. The zlib module is used for compressing the responses. 2. Define Brotli Options: We define Brotli compression options to enhance performance. 3. Set Server Port and Host: We specify the port and host where the server will listen. 4. SSL Options: SSL options including the key and certificate are read from the ssl directory. 5. Create the HTTP/2 Server: We create the HTTP/2 server using the createSecureServer method. If the request URL is /, the server responds with the content of index.html. For other URLs, the server tries to serve the requested file. The response is compressed using Brotli. Start the Server: The server listens on the specified port and host, and logs a message when it’s up and running. Step 2: Run the HTTP/2 Server To start the server, run: node src/server/http2.js You should see a message indicating that the server is running. Open your browser and navigate to https://localhost:8081 to see the server in action. Note that you’ll need to accept the self-signed certificate. Reference For setting up the HTTPS 1.1 server, please refer to our previous blog: Setting Up and Testing an HTTPS 1.1 Server with Node.js. By following these steps, you have now successfully set up and tested an HTTP/2 server using Node.js. This will allow you to leverage the advanced features and performance benefits of HTTP/2 in your applications. Understanding HTTP/2 in Developer Tools When you inspect your network requests in browser developer tools, you may notice that HTTP/2 requests are denoted by “h2” under the “Protocol” column. HTTP/2, also known as “h2,” brings several improvements over HTTP/1.1: Multiplexing: Multiple requests and responses can be sent simultaneously over a single connection. Header Compression: Reduces the overhead of HTTP headers. Server Push: Allows the server to send resources to the client proactively. Stream Prioritization: Enables prioritization of important resources for faster load times. These features contribute to a more efficient and faster web browsing experience.