From bb990ac674181c52eb74860a1984165e7845265e Mon Sep 17 00:00:00 2001 From: Merlin Beutlberger Date: Mon, 8 Dec 2025 16:21:19 +0100 Subject: [PATCH] [FIX] Explicitly bind to IPv4 loopback In some environments, 'localhost' might resolve to the IPv6 loopback address '::1' which is often unexpected. This can especially lead to problems where other tools might resolve 'localhost' differently in the same environment. This change explicitly binds to the IPv4 loopback address '127.0.0.1' instead of resolving the address from 'localhost'. In case remote connections are allowed, no host is specified so that Node.js binds to either the unspecified IPv4 or -IPv6 address (which usually also accepts IPv4 connections) [1] [1] https://nodejs.org/docs/latest-v25.x/api/net.html#serverlistenport-host-backlog-callback --- lib/server.js | 13 +++++++------ test/lib/server/ports.js | 8 ++++---- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/lib/server.js b/lib/server.js index 3b108a55..5ea5ff88 100644 --- a/lib/server.js +++ b/lib/server.js @@ -26,10 +26,11 @@ function _listen(app, port, changePortIfInUse, acceptRemoteConnections) { const options = {}; if (!acceptRemoteConnections) { - options.host = "localhost"; - } + // Unless remote connections are allowed, bind to the IPv4 loopback address + options.host = "127.0.0.1"; + } // If remote connections are allowed, do not set host so the server listens on all supported interfaces - const host = options.host || "127.0.0.1"; + const portScanHost = options.host || "127.0.0.1"; let portMax; if (changePortIfInUse) { portMax = port + 30; @@ -37,7 +38,7 @@ function _listen(app, port, changePortIfInUse, acceptRemoteConnections) { portMax = port; } - portscanner.findAPortNotInUse(port, portMax, host, function(error, foundPort) { + portscanner.findAPortNotInUse(port, portMax, portScanHost, function(error, foundPort) { if (error) { reject(error); return; @@ -49,7 +50,7 @@ function _listen(app, port, changePortIfInUse, acceptRemoteConnections) { `EADDRINUSE: Could not find available ports between ${port} and ${portMax}.`); error.code = "EADDRINUSE"; error.errno = "EADDRINUSE"; - error.address = host; + error.address = portScanHost; error.port = portMax; reject(error); return; @@ -57,7 +58,7 @@ function _listen(app, port, changePortIfInUse, acceptRemoteConnections) { const error = new Error(`EADDRINUSE: Port ${port} is already in use.`); error.code = "EADDRINUSE"; error.errno = "EADDRINUSE"; - error.address = host; + error.address = portScanHost; error.port = portMax; reject(error); return; diff --git a/test/lib/server/ports.js b/test/lib/server/ports.js index cf65d7c5..1d106245 100644 --- a/test/lib/server/ports.js +++ b/test/lib/server/ports.js @@ -54,7 +54,7 @@ test.serial("Start server - Port is already taken and an error occurs", async (t ); t.is( error.address, - "localhost", + "127.0.0.1", "Correct error address" ); t.is( @@ -89,7 +89,7 @@ test.serial("Start server together with node server - Port is already taken and }); t.deepEqual(server.port, nextFoundPort, "Resolves with correct port"); - const request = supertest(`http://localhost:${nextFoundPort}`); + const request = supertest(`http://127.0.0.1:${nextFoundPort}`); const result = await request.get("/index.html"); if (result.error) { t.fail(result.error.text); @@ -181,7 +181,7 @@ test.serial( ); t.is( error.address, - "localhost", + "127.0.0.1", "Correct error address" ); t.is( @@ -213,7 +213,7 @@ test.serial("Start server twice - Port is already taken and the next one is used }); t.deepEqual(serveResult2.port, nextFoundPort, "Resolves with correct port"); - const request = supertest(`http://localhost:${nextFoundPort}`); + const request = supertest(`http://127.0.0.1:${nextFoundPort}`); const result = await request.get("/index.html"); if (result.error) { t.fail(result.error.text);