A development server that answers on the laptop and nowhere else is usually bound to the wrong address. A server that accepts the connection but refuses the page is usually failing a hostname check. Those are different faults with different fixes, and choosing the right configuration depends on who has to reach the workspace and how they arrive.

Three questions decide the answer: who connects, which address they can reach, and whether live reload has to work for them.

Start With the Socket, Not the Config File

Read what the running process actually bound to inside the same network context that started it, because a container or a namespace can own a socket that the host shell cannot see. The listen column answers the first question: local path only, one specific interface, or every interface.

ss -ltn '( sport = :8080 )'
ss -ltnp | grep node

A loopback address is the safe default and useless to a remote client. A wildcard bind is convenient and exposes the port on any interface that has a route, which is wider than most people intend on a machine that also carries a tunnel. A specific address is the middle option, and it only works if the interface owns that address at the moment the socket is created. The distinction matters because two of those three bindings look identical in the browser and behave completely differently once a second interface exists.

Match that observation before touching routes or firewall rules. If the process is listening on the wrong address, no amount of policy routing will make the client reach it.

Choose the Bind Address by Who Must Reach It

If only you on this machine need the page, keep the loopback bind and stop. If one colleague on the same tunnel needs it, bind the tunnel interface address and let the firewall permit only that source. Publishing a development server beyond the people actively testing it is a habit that eventually exposes a workspace to the internet.

If a browser inside a container needs to reach the server, the bind address has to be one the container's network can route to, and that is often not the address the host resolves for itself. Decide the case before editing configuration, because the three cases produce three different values and only one of them is right. Guessing here wastes more time than any other part of the setup.

Then set the address in the form your launcher actually reads. A configuration file and a script that constructs the server programmatically do not consume the same options:

devServer: {
  host: "10.77.0.20",
  port: 8080,
  allowedHosts: ["dev.corp.example"],
}

The address in that fragment is illustrative and stands for the VPN address the host owns. If the project builds the server with new WebpackDevServer(options, compiler), pass the options through the constructor argument instead, because the separate devServer property in the webpack configuration is not consulted on that path. That difference between the configuration file and the Node API catches people who copy a snippet into a project that never reads it. The dev-server documentation separates the two launch styles and is the reference to check against the version in use.

Match the Hostname Check to the Names You Approve

The hostname check exists to stop a page served from an attacker-controlled name from talking to your development server through the browser. Disabling it with allowedHosts: "all" removes that protection, and the convenience is rarely worth it on a machine that can reach internal services. The check is cheap to satisfy correctly once you know which names the browser will send.

List the exact names you will use, including the private name that resolves to the tunnel address. If the client resolves a name to a different address than the one you bound, you have a name resolution problem rather than a server configuration problem, and adding the name to the allowlist will not fix it. Confirm the resolution from the client, not from the server.

Keep the three controls distinct in your head: the bind address decides which interfaces accept the connection, the allowlist decides which Host headers are accepted, and authentication decides who is allowed in. Only the third one is an access control, and a development server usually has none. A tunnel address is not an authentication mechanism either, since anyone who can route to it is already inside the boundary you were relying on.

Verify the Page and the Live-Reload Socket Together

Load a fresh request from the intended client with the configured scheme, and read the result rather than the cache. For HTTPS, keep the expected hostname and certificate validation, because a browser warning is not a successful acceptance test. A page that renders while live reload silently fails is the most common partial success here, and it is easy to miss during a quick check.

curl -sS -o /dev/null -w "%{http_code} %{remote_ip}\n" http://dev.corp.example:8080/
curl -sS -i http://10.77.0.20:8080/ | head -n 5

Then open the browser's network diagnostics and watch the WebSocket connection. A page can load over one hostname, port and scheme while the reload socket uses another, which is why the two have to be checked separately. Configure the documented client URL only when the deployed path genuinely needs a different value, and prefer fixing the path over teaching the client about a workaround.

Record the listener address, the accepted name, the permitted source and both results in the project notes. That record survives the next person who starts the workspace and wonders why the page loads but never refreshes.

متعلقہ مضامین