有时候开发就像一次意外的探险,我本想简单地将端口改为10080,没想到却踩中了Node.js fetch的黑名单。这个小小的失误,让我领悟到了Fetch标准背后的秘密。
有时候开发就是这样,问题不是你想找就能找到,而是你“误打误撞”自己送上门。 这次我们就“中大奖”了:
本来只是想把 80 端口 改成内部用的端口,图省事随手加了个 +100 变成 10080。结果没想到,这个“随机的选择”正好踩中了 Node.js fetch 的 blocked port 黑名单。😂
于是场面就很魔幻:
- 用 Node.js fetch 请求,直接报错:bad port;
- 换成 http 模块 (http.request),却能正常工作。
一开始我们还以为是服务没起来、或者 Node.js 的 bug。一路排查下来,才发现原来这是 Fetch 标准暗中设定的规则,而不是我们代码的问题。
这篇文章,就带你一起复盘这次踩坑:为什么 Node.js fetch 会被端口 block?为什么 http 模块没事?如果遇到类似问题,应该怎么处理?
Node.js Fetch 报错复现:bad port
先看一个最小复现:
import http from "node:http";
const server = http.createServer((req, res) => {
res.writeHead(200, { "Content-Type": "application/json" });
res.end(JSON.stringify({ message: "hello" }));
});
server.listen(10080, "127.0.0.1", async () => {
console.log("✓ Server started on http://127.0.0.1:10080");
// 使用 Node.js fetch
try {
const res = await fetch("http://127.0.0.1:10080/test");
console.log("fetch() result:", await res.text());
} catch (err) {
console.error("fetch() failed:", err.message, err.cause?.message);
}
// 使用 http.request
http.get("http://127.0.0.1:10080/test", (res) => {
res.on("data", (chunk) =>
console.log("http.request() result:", chunk.toString())
);
});
});
运行结果:
✓ Server started on http://127.0.0.1:10080
fetch() failed: fetch failed bad port
http.request() result: {"message":"hello"}
看到了吧?同一个端口,fetch 直接翻车,http 模块却稳稳的。 这下基本能确定,问题不在服务端,而在 fetch 自己身上。
根因:Fetch Standard 的 Port Blocking List
被逼着查了各种文档,直到看到 WHATWG Fetch Standard,才找到答案!
原来 fetch 内置了一份端口阻止列表(Port Blocking List),出于安全原因,直接拒绝访问这些端口。
常见被阻止的端口包括:
| 端口号 | 服务 | 是否被 fetch 阻止 |
|---|---|---|
| 25 | SMTP 邮件服务 | ✅ |
| 110 | POP3 邮件服务 | ✅ |
| 143 | IMAP 邮件服务 | ✅ |
| 6667/6697 | IRC 聊天服务 | ✅ |
| 6000 | X11 | ✅ |
| 10080 | Amanda 备份服务 | ✅ |
所以 Node.js 内置的 fetch(基于 undici 实现)忠实执行了标准,访问这些端口时就直接抛出:
TypeError: fetch failed
Cause: bad port
而 http 模块 根本没管这回事,结果就是 —— fetch 说“不行”,http 模块却说“没问题”。
解决办法:要么换端口,要么换工具
既然这是标准规定的“暗中规则”,那解决方案就很简单:
-
能换端口就换端口
避开这些黑名单里的端口,比如用 3000、8080、18080 之类的 -
不能换端口?那就换工具
如果必须访问这些端口,请求时不要用 fetch,可以改用:- 用 Node.js 原生的 http.request / http.get;
- 或者用第三方库:axios、got。
示例:
import http from "node:http"; http.get("http://127.0.0.1:10080/test", (res) => { res.on("data", (chunk) => console.log(chunk.toString())); }); -
不要想着绕过标准
Node.js 没有开关能关掉这个限制,因为这是 Fetch Standard 自带的规定。
总结
这次算是一次“误打误撞”的发现:
- Node.js fetch blocked port 并不是 bug,而是 Fetch 标准规定的行为。
- 如果遇到 fetch failed: bad port,第一时间应该想到是不是踩中了 Port Blocking List。
- http 模块 不受限制,可以作为替代方案。
说白了,这次我们还挺“幸运”的:本来只是图省事,把 80 改成 10080,结果正好踩中黑名单。 就像买彩票 —— 奖倒是中了,只不过奖品是一份 bad port 报错。🎁😂
👉 下次再遇到奇怪的 fetch failed,别先怀疑人生,说不定你也中了大奖。
如果这篇文章对你有帮助,或者让你有新的想法,欢迎留言!