From c8cfe8934735637211faf9d2d0f37c80d64489d8 Mon Sep 17 00:00:00 2001 From: Merlin Beutlberger Date: Tue, 7 Apr 2026 16:41:48 +0200 Subject: [PATCH] fix(server): 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 Cherry-pick of https://github.com/SAP/ui5-server/pull/763 --- packages/server/lib/server.js | 13 +++++++------ packages/server/test/lib/server/ports.js | 8 ++++---- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/packages/server/lib/server.js b/packages/server/lib/server.js index 3b108a551d7..5ea5ff8812d 100644 --- a/packages/server/lib/server.js +++ b/packages/server/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/packages/server/test/lib/server/ports.js b/packages/server/test/lib/server/ports.js index cf65d7c5ad2..1d10624557d 100644 --- a/packages/server/test/lib/server/ports.js +++ b/packages/server/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);