嘿, 我是Mofei!
16
误打误撞踩到 Node.js Fetch Blocked Port 的坑:为什么 fetch 不工作,而 http 模块却能正常工作?
2025年10月3日 17:07

有时候开发就是这样,问题不是你想找就能找到,而是你“误打误撞”自己送上门。 这次我们就“中大奖”了:

本来只是想把 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 阻止
25SMTP 邮件服务
110POP3 邮件服务
143IMAP 邮件服务
6667/6697IRC 聊天服务
6000X11
10080Amanda 备份服务

所以 Node.js 内置的 fetch(基于 undici 实现)忠实执行了标准,访问这些端口时就直接抛出:

TypeError: fetch failed
Cause: bad port

而 http 模块 根本没管这回事,结果就是 —— fetch 说“不行”,http 模块却说“没问题”。


解决办法:要么换端口,要么换工具

既然这是标准规定的“暗中规则”,那解决方案就很简单:

  1. 能换端口就换端口
    避开这些黑名单里的端口,比如用 3000、8080、18080 之类的

  2. 不能换端口?那就换工具
    如果必须访问这些端口,请求时不要用 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()));
    });
    
  3. 不要想着绕过标准

    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,别先怀疑人生,说不定你也中了大奖。

📱 喜欢这篇文章?

博客文章会第一时间发布,然后按类型同步到对应公众号

THE END

如果觉得文章有值得讨论的地方,欢迎留言和我交流!

Mofei's Friend

avatar
今天有什么好玩的事?
还没有评论
成为第一个评论的人吧!

HI. I AM MOFEI!

NICE TO MEET YOU!