有时候开发就是这样,问题不是你想找就能找到,而是你“误打误撞”自己送上门。 这次我们就“中大奖”了:
本来只是想把 80 端口 改成内部用的端口,图省事随手加了个 +100 变成 10080。结果没想到,这个“随机的选择”正好踩中了 Node.js fetch 的 blocked port 黑名单。😂
于是场面就很魔幻:
一开始我们还以为是服务没起来、或者 Node.js 的 bug。一路排查下来,才发现原来这是 Fetch 标准暗中设定的规则,而不是我们代码的问题。
这篇文章,就带你一起复盘这次踩坑:为什么 Node.js fetch 会被端口 block?为什么 http 模块没事?如果遇到类似问题,应该怎么处理?
先看一个最小复现:
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 自己身上。
被逼着查了各种文档,直到看到 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,可以改用:
示例:
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 自带的规定。
这次算是一次“误打误撞”的发现:
说白了,这次我们还挺“幸运”的:本来只是图省事,把 80 改成 10080,结果正好踩中黑名单。 就像买彩票 —— 奖倒是中了,只不过奖品是一份 bad port 报错。🎁😂
👉 下次再遇到奇怪的 fetch failed,别先怀疑人生,说不定你也中了大奖。
博客文章会第一时间发布,然后按类型同步到对应公众号
如果觉得文章有值得讨论的地方,欢迎留言和我交流!