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
| Port | Usual use | Chromium navigation |
|---|---|---|
| 1 | tcpmux | net::ERR_UNSAFE_PORT |
| 21 | FTP | net::ERR_UNSAFE_PORT |
| 22 | SSH | net::ERR_UNSAFE_PORT |
| 25 | SMTP | net::ERR_UNSAFE_PORT |
| 110 | POP3 | net::ERR_UNSAFE_PORT |
| 143 | IMAP | net::ERR_UNSAFE_PORT |
| 2049 | NFS | net::ERR_UNSAFE_PORT |
| 3659 | Apple SASL | net::ERR_UNSAFE_PORT |
| 4045 | NFS lockd | net::ERR_UNSAFE_PORT |
| 5060 | SIP | net::ERR_UNSAFE_PORT |
| 6000 | X11 | net::ERR_UNSAFE_PORT |
| 6566 | SANE scanner | net::ERR_UNSAFE_PORT |
| 6665, 6666, 6667, 6668, 6669 | IRC | net::ERR_UNSAFE_PORT |
| 6697 | IRC over TLS | net::ERR_UNSAFE_PORT |
| 10080 | Amanda backup | net::ERR_UNSAFE_PORT |
| 4000, 5000, 5173, 8080 | common dev ports | net::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
| Client | Port 6000 (nothing listening) | Port 8080 (nothing listening) |
|---|---|---|
| Chromium 153 navigation | net::ERR_UNSAFE_PORT | net::ERR_CONNECTION_REFUSED |
| Chromium 153 fetch() | TypeError: Failed to fetch | TypeError: Failed to fetch |
| Node.js 24 fetch() | TypeError: fetch failed, cause: Error "bad port" | TypeError: fetch failed, cause: ECONNREFUSED |
| Node.js 24 http.get | ECONNREFUSED (not blocked) | ECONNREFUSED |
| curl 8.14.1 | exit 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 urllib | ConnectionRefusedError(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
- Move your dev server to a port that is not on the list, such as 4000, 5000, 5173 or 8080. All of the ones we tested gave
ERR_CONNECTION_REFUSED, notERR_UNSAFE_PORT, so the browser was willing to connect. - For a public site, serve on 80 and 443 and put the app behind a reverse proxy. Then the backend port does not matter to browsers.
- Watch for "random" ports. 6000 to 6697 and 10080 are easy to land on by accident with
PORT=6000or an IRC bridge. - If the error turns into
ERR_CONNECTION_REFUSEDafter the move, the block is gone and nothing is listening. See ERR_CONNECTION_REFUSED / ECONNREFUSED.
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.