net::ERR_UNSAFE_PORT (tested)

net::ERR_UNSAFE_PORT means the browser refused to connect because the port is on its blocked list. Nothing went over the network. Your server may be fine. We tested which ports Chromium 153 blocks, what fetch() reports, and that Node.js fetch blocks the same ports while curl does not.

1. What ERR_UNSAFE_PORT means

Browsers keep a list of "bad ports". These are ports used by other protocols, such as SSH (22), SMTP (25), IRC (6665-6669) and X11 (6000). A web page could otherwise trick the browser into sending HTTP-shaped data to those services. So the browser refuses before it opens a socket.

The key clue: with nothing listening on the port, a normal port gives ERR_CONNECTION_REFUSED. A blocked port gives ERR_UNSAFE_PORT. The browser never even tried.

2. Our test setup

We pointed headless Chromium 153 at http://127.0.0.1:<port>/ for each port, with no server on any of them. For each port we did one navigation (page.goto) and one fetch() from a page. Then we did the same from Node.js 24, curl and Python.

3. Results: which ports Chromium 153 blocks

PortUsual useChromium navigation
1tcpmuxnet::ERR_UNSAFE_PORT
21FTPnet::ERR_UNSAFE_PORT
22SSHnet::ERR_UNSAFE_PORT
25SMTPnet::ERR_UNSAFE_PORT
110POP3net::ERR_UNSAFE_PORT
143IMAPnet::ERR_UNSAFE_PORT
2049NFSnet::ERR_UNSAFE_PORT
3659Apple SASLnet::ERR_UNSAFE_PORT
4045NFS lockdnet::ERR_UNSAFE_PORT
5060SIPnet::ERR_UNSAFE_PORT
6000X11net::ERR_UNSAFE_PORT
6566SANE scannernet::ERR_UNSAFE_PORT
6665, 6666, 6667, 6668, 6669IRCnet::ERR_UNSAFE_PORT
6697IRC over TLSnet::ERR_UNSAFE_PORT
10080Amanda backupnet::ERR_UNSAFE_PORT
4000, 5000, 5173, 8080common dev portsnet::ERR_CONNECTION_REFUSED (allowed)

The same block applied to https://127.0.0.1:6000/ (net::ERR_UNSAFE_PORT) and to a WebSocket on ws://127.0.0.1:6000/. The WebSocket closed with code 1006 and the console said:

WebSocket connection to 'ws://127.0.0.1:6000/' failed: Error in connection establishment: net::ERR_UNSAFE_PORT

4. What fetch() throws

In the page, fetch() to a blocked port threw only TypeError: Failed to fetch. That is the exact same error as a refused port or a CORS failure. It failed in about 3 ms. The real reason only appears in the DevTools console:

Failed to load resource: net::ERR_UNSAFE_PORT

So if your JavaScript only logs the error, you will see "Failed to fetch" and nothing more. Open the console or the Network tab. See TypeError: Failed to fetch for the other causes of that message.

5. Node.js fetch blocks too, curl does not

ClientPort 6000 (nothing listening)Port 8080 (nothing listening)
Chromium 153 navigationnet::ERR_UNSAFE_PORTnet::ERR_CONNECTION_REFUSED
Chromium 153 fetch()TypeError: Failed to fetchTypeError: Failed to fetch
Node.js 24 fetch()TypeError: fetch failed, cause: Error "bad port"TypeError: fetch failed, cause: ECONNREFUSED
Node.js 24 http.getECONNREFUSED (not blocked)ECONNREFUSED
curl 8.14.1exit 7, "Failed to connect to 127.0.0.1 port 6000 after 0 ms: Could not connect to server" (not blocked)exit 7, same
Python urllibConnectionRefusedError(111) (not blocked)ConnectionRefusedError(111)

Node.js fetch() follows the same Fetch standard bad-port list as browsers. It rejected ports 1, 2049, 5060, 6000, 6665 and 10080 with cause.message = bad port and no error code. The older http module has no such list. Always print err.cause in Node:

try {
  await fetch('http://127.0.0.1:6000/');
} catch (err) {
  console.log(err.message, '|', err.cause?.message);
  // fetch failed | bad port
}

This explains a common confusion: "curl works but the browser and fetch fail". curl does not check ports at all.

6. The Chromium flag that unblocks a port

Chromium accepts --explicitly-allowed-ports. We launched it with --explicitly-allowed-ports=6000,10080. Navigation to 6000 and 10080 changed from ERR_UNSAFE_PORT to ERR_CONNECTION_REFUSED, so the block was lifted. Port 6665, not in the flag, was still blocked.

# Chrome / Chromium
google-chrome --explicitly-allowed-ports=6000,10080

# Playwright
chromium.launch({ args: ['--explicitly-allowed-ports=6000'] });

This only fixes your own browser. Your users will still be blocked. Use it for local testing, not as a fix.

Firefox has a similar setting, network.security.ports.banned.override, in about:config. It takes a comma-separated list of ports. That is per the Firefox docs, not measured here. Firefox shows its own error page ("This address is restricted") rather than the Chromium code.

7. The real fix: pick a different port

Frequently asked questions

Why does Chrome say ERR_UNSAFE_PORT when my server is running?

Chrome refused before connecting, so your server is never reached. The port is on the browser's bad-port list. In Chromium 153 we saw it on ports such as 6000, 6665-6669, 10080, 5060 and 22.

Why does curl work but the browser does not?

curl has no port block list. In our test curl tried port 6000 and got a normal connection refused. Chromium and Node.js fetch refused the port without trying.

How do I allow a blocked port in Chrome?

Start Chrome with --explicitly-allowed-ports=6000 (comma-separate several ports). In our test this changed port 6000 from ERR_UNSAFE_PORT to a normal connection attempt. It only affects that browser, so it is not a fix for users.

Does Node.js fetch block unsafe ports?

Yes. Node.js 24 fetch threw TypeError: fetch failed with cause "bad port" for 6000, 10080 and others. The http module and http.get did not block, and got ECONNREFUSED.

What does fetch() show for an unsafe port in the browser?

Only TypeError: Failed to fetch. The net::ERR_UNSAFE_PORT detail appears in the DevTools console and Network tab, not in the error object.

Written with AI assistance. Measured on 2026-10-02 with headless Chromium 153.0.8010.12 (Playwright), Node.js 24.21.0 fetch and http.get, curl 8.14.1 and Python 3 urllib, against 127.0.0.1 with nothing listening on the tested ports. The Firefox parts are from the Firefox docs, not measured. PlainHeaders is our extension; Chromium, Node.js, curl and Python are free.