分析阅读约 8 分钟

NO_PROXY 通配符、前导点与 CIDR:实测矩阵

哪些 NO_PROXY 条目在 curl、Python、Node、Go 和 wget 中含义相同?一次针对 *、前导点、CIDR、IPv6 和端口的回环测试,附原始数据。

本页内容

NO_PROXY 没有标准语法,读取它的客户端在大多数细节上意见不一。在 2026 年 9 月 23 日一次回环实验的主矩阵中,15 个客户端收到了完全相同的 no_proxyNO_PROXY 值:两个 curl 构建、wget、Go、五个 Python 客户端和六种 Node.js 配置。主矩阵中的 49 种「值 + 请求」组合里,21 种在所有客户端中路由一致,28 种不一致。每个单元格都在已发布的 results.csv 中,每个客户端和请求各占一行。

如果一个值必须同时服务于所有这些客户端,请只使用在各处表现完全一致的写法,并给两个变量名赋相同的值:

sh
# example.com and 192.0.2.10 stand for your own domain and address
export no_proxy="localhost,127.0.0.1,example.com,.example.com,192.0.2.10"
export NO_PROXY="$no_proxy"
  • example.com,.example.com 这一对(测试时为 example.test,.example.test)是唯一在全部 15 个客户端中同时覆盖一个域名及其所有子域名的写法。它从未匹配 notexample.test
  • 精确的 IPv4 地址在每个客户端中都只匹配自身。IPv4 CIDR 范围只在 15 个客户端中的 4 个里有效。
  • localhost,127.0.0.1 在每个客户端中都让 127.0.0.1localhost 绕过了代理。不列出回环条目时,只有 Go 会为回环地址跳过代理。
  • wget 只读取 no_proxy。两者都设置时,Go 1.27.1 的 net/http 优先取 NO_PROXY,而 Go 1.28 预计将改为优先取 no_proxy。保持两个名称的值一致。

已发布的数据集是在各自独立的用例中测试这些条目的。在一次对上面这行原样运行的一次性检查中(用 example.test 代替 example.com),它的九个测试请求在全部 15 个客户端中也都路由一致。

有两类写法会带来麻烦。一类会让客户端出错,或让整个列表失效。带方括号的 IPv6 条目会让 httpx 和 httpx2 在创建客户端时失败,httpx 0.28.1 中的 IPv6 CIDR 条目也是如此。在设置了 NO_PROXY 的同时把小写 no_proxy 设为空,会让 9 个客户端关闭整个列表。另一类会被某些客户端静默忽略:*.example.com 被 8 个忽略,列表内的 * 被 9 个忽略,IPv4 CIDR 范围被 11 个忽略,尾随点被 11 个忽略,空格分隔的列表被 9 个忽略,host:port 条目视其形式被 4 到 6 个忽略。就连单独的 * 也会被 wget 忽略。

这些结果是合成的。它们来自一台 Mac、一个位于 127.0.0.1 的代理、保留的 .test 域名和文档用 IP 地址,描述的是固定版本的客户端如何选择路由,而不是任何代理服务的行为。关于各客户端最初是如何读取代理变量的,见客户端如何读取 HTTP_PROXY 和 NO_PROXY。如果 Node 的 fetch() 完全忽略了代理,请先阅读修复 Node.js 代理后的「TypeError: fetch failed」

可移植的子集与应避免的写法

表格使用实验中请求的名称:example.testsub.example.testa.b.example.testnotexample.test。这里没有任何客户端会在决定路由之前解析主机名:它们的源码把名称当作文本比较,而 curl、Go 和 Requests 会解析 URL 中的 IP 地址用于 CIDR 检查。662 个经代理的请求中,没有一个触发了沙箱记录在案的 DNS 查询或连接拒绝,尽管内核日志会丢弃部分拒绝记录。因此真实域名的行为应当与这些名称一致。

这 15 个客户端各自以所示设置从环境中读取代理变量,它们是:

  • curl 8.7.1(macOS)和 curl 8.22.0;
  • GNU Wget 1.25.0;
  • Go 1.27.1 net/http,使用 http.ProxyFromEnvironment
  • Python 3.14.7 urllib、requests 2.34.2、httpx 0.28.1、httpx2 2.13.1 和 aiohttp 3.14.3,均设置 trust_env=True
  • Node.js 26.10.0 和 24.21.0 的内置 fetch()http.get(),各自设置 NODE_USE_ENV_PROXY=1(下文称为「Node 26 fetch」「Node 26 http」等);
  • npm undici 7.29.1 和 8.11.0 的 fetch(),使用 dispatcher: new EnvHttpProxyAgent(),均运行在 Node 26.10.0 上。

对于所示请求,这些条目在全部 15 个客户端中给出了相同的结果:

条目在每个客户端中的结果
example.test绕过了 example.testnotexample.test 仍走代理(子域名在 Node 的 http 客户端中有差异)
.example.test绕过了 sub.example.testa.b.example.test(apex 域名在 4 个客户端中有差异)
example.test,.example.test绕过了 apex 域名和两个子域名;notexample.test 仍走代理
192.0.2.10绕过了该地址;192.0.2.11 仍走代理
EXAMPLE.TEST,或对 EXAMPLE.TEST 的请求大小写没有影响
other.test , example.test逗号后的空格没有影响
other.test,,example.testother.test,,空项不匹配任何内容
localhost,127.0.0.1,::1绕过了 127.0.0.1localhost[::1] 有差异;见回环一节)
只设置 no_proxy,不设置 NO_PROXY每个客户端都读取了小写变量

上文答案中每一种应避免的写法在下面都有单独的一节,并列出了行为不一致的客户端。

NO_PROXY 通配符:*.example.com 是否匹配 example.com

这取决于客户端,而且 15 个客户端中有 8 个根本不把它当作通配符。设置 NO_PROXY=*.example.test 时:

请求绕过了代理走了代理
example.testNode 26 fetch、Node 24 fetch、undici 7.29.1其余 12 个
sub.example.testa.b.example.testGo、Node 26 和 24 的 fetch 与 http、undici 7.29.1 和 8.11.0curl(两个)、wget、urllib、requests、httpx、httpx2、aiohttp
notexample.test全部 15 个

curl、wget 和全部五个 Python 客户端用这个条目什么也没匹配到。Go、Node 的 http 客户端和 undici 8.11.0 把它理解为「仅子域名」,而 Node 内置的 fetch() 和 undici 7.29.1 还绕过了 apex 域名。

Node 26 内置 fetch() 与 npm undici 8.11.0 在 apex 域名上的分歧是新出现的。undici PR #5777 于 2026 年 9 月 22 日随 undici 8.11.0 发布,其中说明 *.domain 条目「必须只匹配 sub.example.com / a.b.example.com,而不匹配 apex 域名」。Node 26.10.0 仍然捆绑 undici 8.10.2。因此在同一个 Node 26.10.0 运行时上,内置 fetch() 绕过了 example.test,而 npm undici 8.11.0 把它发往了代理。同一个 pull request 还让 * 在列表内或前后带空格时也能生效,这解释了这两个客户端之间仅有的其他几行差异。

要在每个客户端中都匹配子域名,请写 .example.com。如果 apex 域名也应绕过代理,再加上 example.com。没有任何一种测试过的写法能在全部 15 个客户端中既让子域名绕过代理,又让 apex 域名继续走代理。

前导点与不带点的域名:.example.comexample.com 与子域名

条目与请求绕过了代理走了代理
example.test,请求 sub.example.test13 个客户端Node 26 http、Node 24 http
.example.test,请求 example.test11 个客户端wget、Go、httpx、httpx2

不带前导点的域名在所有客户端中都匹配其子域名,唯独 Node 内置的 http 客户端例外,它只精确匹配 example.test。实验在 26.10.0 和 24.21.0 上复现了最初在 Node 24.18.1 上报告的 node#65616。Node 自己的 NO_PROXY 格式列表example.com 称为「Exact host name match」(精确主机名匹配),这描述的是 http.request(),而不是 fetch()。修复 node#65617 截至 2026 年 9 月 23 日仍未合并。

前导点在每个客户端中都匹配所有子域名,但 wget、Go、httpx 和 httpx2 不把它应用于 apex 域名。Go 的文档明确了这一点:带前导点的域名「仅匹配子域名」(httpproxy)。

两种条目在任何客户端中都没有匹配 notexample.test。Requests 直到 2.34.0 才开始强制这条标签边界;其发布说明称它「不再对 no_proxy 域名做贪婪匹配」。更早的 Requests 版本未经测试。

NO_PROXY=* 与列表内的 *

绕过了代理走了代理
*14 个客户端wget
other.test,*" * "Go、httpx、httpx2、Node 26 http、Node 24 http、undici 8.11.0curl(两个)、wget、urllib、requests、aiohttp、Node 26 fetch、Node 24 fetch、undici 7.29.1

* 作为整个值、不带空格时,在除 wget 之外的每个客户端中都有效。curl 的文档就是这样写的:「唯一可用的通配符是单个 * 字符」(CURLOPT_NOPROXY)。wget 1.25.0 没有通配符。要让某一条 wget 命令不走代理,请使用它的 --no-proxy 选项(wget 手册)。

NO_PROXY 中的 CIDR、IP 范围与子网

设置 NO_PROXY=192.0.2.0/24 时,对 192.0.2.10 的请求在 curl 8.7.1、curl 8.22.0、Go 和 requests 中绕过了代理。其余 11 个把它发往了代理:wget、urllib、httpx、httpx2、aiohttp 和全部六个 Node 客户端。范围之外的 198.51.100.10 在每个客户端中都走了代理。没有客户端因 IPv4 范围而报错;11 个不支持它的客户端直接忽略了它。

  • curl 在 7.86.0 中加入了 CIDR 支持(变更日志)。
  • Requests 在回退到 urllib 之前先用自己的匹配器检查 CIDR,这就是两个 Python 客户端结果不同的原因。
  • httpx 和 httpx2 接受了该条目,但没有匹配范围。将会加入 CIDR 范围支持的 httpx2 PR #1165 截至 9 月 23 日仍未合并。
  • Node 的 NO_PROXY 格式列表记录的是 192.168.1.1-192.168.1.100 这样的连字符范围,而不是 CIDR。实验没有测试连字符范围。node#57872 的一条评论报告,在 v27 预发布版本上,连字符范围在 node:http 中有效,但在 fetch() 中无效。

要在混合技术栈中让一个子网不走代理:

  • 列出你的客户端实际调用的确切地址。精确的 IPv4 地址是唯一在全部 15 个客户端中都匹配的形式。
  • 可以在它们旁边为 curl、Go 和 Requests 加上 IPv4 范围。设置 192.0.2.10,192.0.2.0/24 时,一次不在已发布数据集内的一次性运行在全部 15 个客户端中绕过了 192.0.2.10,而 192.0.2.11 只在那 4 个客户端中绕过;没有客户端报错。不要加入 IPv6 范围,因为 httpx 0.28.1 会因此失败(见下文)。
  • 范围只适用于 URL 中直接写出的 IP 地址。curl、Go 和 Requests 不会先解析主机名,所以 192.0.2.0/24 不覆盖一个解析到该范围内的名称。这一点来自它们的源码;实验只使用了 IP 地址。

NO_PROXY 中的 IPv6:不带方括号、带方括号与 CIDR

对于访问 http://[2001:db8::10]/ 的请求:

条目绕过了代理未绕过
2001:db8::1012 个客户端urllib、Node 24 fetch、undici 7.29.1 走了代理
[2001:db8::10]urllib、Node 26 fetch、Node 24 fetch、undici 7.29.1、undici 8.11.08 个走了代理;httpx 和 httpx2 报错
2001:db8::/48curl 8.22.0、Go12 个走了代理;httpx 报错
[2001:db8::10]:8080,请求端口 8080Go、urllib、Node 26 fetch、Node 24 fetch、undici 7.29.1、undici 8.11.07 个走了代理;httpx 和 httpx2 报错

不带方括号的形式表现最好:它在 12 个客户端中匹配,且没有让任何客户端出错。curl 就要求这样写:「在主机名列表中输入 IPv6 数字地址时不要加方括号」(CURLOPT_NOPROXY)。undici 在 8.10.0 中加入了不带方括号的 IPv6 匹配(PR #5623);Node 24.21.0 捆绑的 undici 7.29.1 没有这项功能。

两个 curl 构建中,只有 8.22.0 匹配了 IPv6 范围。在 8.18.0 之前(curl#19828),curl 的匹配器只在主机带着方括号到达时才把它当作 IPv6,而这种情况从不会发生,所以 IPv6 CIDR 条目无法匹配。

带方括号的 IPv6 条目导致 httpx 抛出 InvalidURL: Invalid port

在 httpx 0.28.1 和 httpx2 2.13.1 中,带方括号的条目比被忽略更糟。以默认的环境处理方式创建客户端时,在发送任何请求之前就抛出了 InvalidURL: Invalid port: 'db8::10]'。设置 NO_PROXY=localhost,127.0.0.1,[::1] 时,错误是 Invalid port: ':1]',而且对 127.0.0.1localhost 的请求也同样失败,因为客户端根本没有创建成功。httpx 0.28.1 对 2001:db8::/48 也以同样的方式报错(Invalid port: 'db8::')。httpx2 2.5.0 消除了这个崩溃(「Allow IPv6 CIDR notation in no_proxy」,变更日志),但如表所示,它仍然没有匹配该范围。

要修复它,请把 IPv6 条目写成不带方括号的形式,例如 ::12001:db8::10;这样两个客户端都能正常创建。不要在 httpx 0.28.1 会读取的任何值中放入 IPv6 范围。如果你无法修改这个变量,就用 trust_env=False 创建该客户端(这样也能正常创建),并像 HTTPX 代理指南那样在代码中传入代理。这样创建的客户端会忽略所有代理变量。

NO_PROXY 中的端口:host:port

对于访问所列端口 8080 的请求:

条目绕过了代理走了代理
example.test:808011 个客户端curl(两个)、wget、aiohttp
192.0.2.10:808010 个客户端curl(两个)、wget、requests、aiohttp
.example.test:80809 个客户端curl(两个)、wget、aiohttp、Node 26 http、Node 24 http

在端口 80 上,三个条目在每个客户端中都走了代理,因此没有客户端把端口条目应用到不同的端口。问题出在相反的方向:有些客户端根本从不匹配这类条目。curl 的文档没有描述条目的端口语法,wget 只比较主机名。aiohttp 把不带端口的主机传给 urllib 的匹配器(源码)。Requests 从不匹配带端口的 IPv4 条目:设置 192.0.2.10:8080 时,它在端口 8080 和端口 80 上都走了代理。据 PR #7586 所述,它的 IPv4 检查只比较主机;该修复截至 9 月 23 日仍未合并。

不要在共享的值中写端口。如果某个端口必须绕过代理,请在需要它的那个客户端中配置该绕过规则。

空格、空项、尾随点与大写

  • **逗号前的空格:**设置 example.test ,other.test 时,wget 把尾随空格保留为条目的一部分,并把 example.test 发往了代理。其余 14 个绕过了它。
  • **用空格代替逗号:**设置 other.test example.test 时,curl 8.7.1、Node 26 fetch、Node 24 fetch、undici 7.29.1 和 undici 8.11.0 绕过了两个主机。curl 8.22.0 只绕过了 other.test。其余 9 个两个都没有绕过。curl 8.9.0 做出了这一改动:「noproxy: patterns need to be comma separated」(变更日志)。
  • 空项:other.test,, 在任何客户端中都没有绕过 example.test,因此没有客户端把空项当作「匹配一切」。
  • 尾随点:example.test. 条目,或对 http://example.test./ 的请求,只在 curl 8.7.1、curl 8.22.0、Node 26 fetch 和 undici 8.11.0 中匹配。undici 在 8.10.1 中加入了这一点(PR #5637),所以 Node 24 fetch 和 undici 7.29.1 没有它。
  • **大写:**大写的条目和主机在全部 15 个客户端中都匹配。

NO_PROXYno_proxy:谁优先,以及空变量陷阱

对于访问 example.test 的请求:

环境绕过了代理走了代理
只设置 NO_PROXY=example.test14 个客户端wget
只设置 no_proxy=example.test全部 15 个
NO_PROXY=example.testno_proxy=other.testGo 1.27.1其余 14 个
NO_PROXY=example.testno_proxy=""curl(两个)、Go、requests、Node 26 http、Node 24 httpwget、urllib、httpx、httpx2、aiohttp、Node 26 fetch、Node 24 fetch、undici 7.29.1、undici 8.11.0

当两个名称的值不一致时,Go 1.27.1 的 net/http 使用 NO_PROXY,其余 14 个使用 no_proxy。空的小写变量把客户端分成了 6 比 9 两组。curl、Go、requests 和 Node 的 http 客户端把它当作未设置,并回退到 NO_PROXY。其余九个把它当作空列表。2026 年 9 月 22 日针对预发布版本提交的 node#66202 描述了 fetch()http.request() 之间的这一分歧。实验在正式发布的 Node 26.10.0 和 24.21.0 上复现了它。

Go 的优先级正在改变。golang.org/x/net 的提交 a02ddfa7eagolang/go#79656 而作,并随 x/net v0.58.0 发布,它让 httpproxy 包优先取小写名称,而 Go 1.28 发布说明草案ProxyFromEnvironment 列出了同样的改动。在一次单独的检查中,x/net v0.58.0 和 v0.59.0 在冲突情形下已经遵循 no_proxy。Go 仍然跳过空值,因此它在空变量上的结果不会改变。

代理变量也有同样的陷阱。设置 http_proxy="" 并设置 HTTP_PROXY 时,只有 Go、Node 26 http 和 Node 24 http 走了代理;其余 12 个直连。curl 只读取小写的 http_proxy

给两个名称赋相同的值,并用 unset 移除变量,而不是把它导出为空值。

localhost、127.0.0.1 与 ::1:只有 Go 会自行跳过代理

完全不设置 NO_PROXY 时,Go 对 127.0.0.1localhost[::1] 都直连。它的 httpproxy 包记录了 localhost 和回环地址从不使用代理。其余 14 个客户端把这三个都发往了代理。因此在配置了远程代理时,除非列出回环地址,这些客户端会把发给本地开发服务器的请求发往那个代理。

127.0.0.1localhost[::1]
localhost,127.0.0.1,::1全部 15 个绕过(Go:内置回环规则)12 个绕过(Go:内置回环规则);urllib、Node 24 fetch 和 undici 7.29.1 走了代理
localhost,127.0.0.1,[::1]httpx 和 httpx2 报错;13 个绕过(Go:内置回环规则)urllib、Node 26 fetch、Node 24 fetch、undici 7.29.1 和 8.11.0 绕过,Go 凭其内置回环规则绕过;httpx 和 httpx2 报错;7 个走了代理

这张表中 Go 的绕过来自其内置规则,而不是来自列表,因此它们不能说明 [::1] 作为条目在 Go 中有效;在主矩阵中,Go 对带方括号的条目 [2001:db8::10] 走了代理。

请列出 localhost,127.0.0.1。如果你使用 IPv6 回环,再加上不带方括号的 ::1,绝不要写 [::1]

经 CONNECT 的 HTTPS 是否得到同样的绕过决定?

在这次运行中,是的。对于 8 个 HTTPS URL 中的每一个,每个客户端都做出了与对应 HTTP URL 相同的选择,120 次比较全部一致。每个经代理的 HTTPS 请求都以 CONNECT 的形式到达代理。

Node.js:NO_PROXY 在 fetch 或 http.request 中不起作用

有四种互不相关的情况会让 NO_PROXY 在 Node 中看起来失效。显式 ProxyAgent 的情形来自 undici 的文档,未经测试;其余三种是实验结果。

**需要显式启用。**没有 NODE_USE_ENV_PROXY=1--use-env-proxy文档)时,Node 26 和 24 的 fetch()http.get() 在全部 36 个对照单元格中都忽略了代理变量并直连。Node fetch 指南覆盖了这种故障。

**显式的 ProxyAgent。**undici 的文档把 ProxyAgent 描述为将每个请求都经其代理路由,并且没有提到绕过列表;读取 no_proxyNO_PROXYEnvHttpProxyAgent 额外提供的能力(undici 8.11.0 的 ProxyAgentEnvHttpProxyAgent 文档)。因此,像 Node.js fetch 代理配置指南那样把 ProxyAgent 作为 dispatcher 传入的代码会忽略 NO_PROXY。要保留绕过列表,请使用 new EnvHttpProxyAgent(),它会读取这些变量,或者接受 httpProxyhttpsProxynoProxy 选项。

同一运行时中的两个匹配器。http.request() 使用 Node 自己的匹配器,而内置 fetch() 使用 undici 的 EnvHttpProxyAgent。在 Node 26.10.0 上,它们在 72 行非基线数据中有 20 行不一致;在 Node 24.21.0 上是 72 行中的 19 行。Node 的匹配器带有注释 TODO(joyeecheung): share code with undici.源码),而共享这部分代码在跟踪 issue node#57872 中仍是一个未完成事项。

**捆绑的 undici 与 npm undici。**在 Node 26.10.0 上,内置 fetch()(undici 8.10.2)与 npm undici 8.11.0 在 4 行上不一致,全部是通配符用例。Node 24.21.0 的 fetch() 与 npm undici 7.29.1 完全没有差异。undici 7.29.1 与 8.11.0 在 11 行上不一致。

下表针对 Node 26.10.0 上的每一类差异各展示一行,使用上文各节中的值。20 行差异中的其余 12 行是这些类型在其他主机和请求上的重复,外加 http_proxy="" 的情形。

差异fetchhttpundici 8.11.0
不带点的域名,请求子域名绕过走代理绕过
*.example.test,请求 apex 域名绕过走代理走代理
列表内的 *走代理绕过绕过
带方括号的 IPv6 地址绕过走代理绕过
带端口的前导点条目绕过走代理绕过
尾随点条目绕过走代理绕过
用空格代替逗号绕过走代理绕过
no_proxy 为空,NO_PROXY 已设置走代理绕过走代理

Node 24.21.0 表现出同样的模式,只有两处例外。对于尾随点,它的两个客户端都走了代理。对于不带方括号的 IPv6 条目,只有 http 绕过了它。

node#57872 上 2026 年 9 月 1 日的一条评论已经在 v27.0.0 预发布版本上比较了 node:httpfetch()。实验的 Node 数据行与那张表中可以比较的全部 8 行一致。

GitLab 在 2021 年的发现,以及此后的变化

GitLab 的 Stan Hu 2021 年的文章(研究由 Nourdin el Bacha 完成)根据源码和文档比较了 curl、wget、Ruby、Python、Go 和 Java。它推荐了一个最小公分母,并提出了一项标准。

它的 no_proxy 表格中,本实验能够核对的 32 个单元格(curl、wget、经 urllib 的 Python 和 Go 各 8 行)里,31 个仍然吻合。例外是 curl 与 CIDR:2021 年的表格说不支持,但两个 curl 构建在这里都遵守了它。Go 的「Uppercase」优先级单元格仍与 Go 1.27.1 相符,并预计在 Go 1.28 中改变。实验无法核对「Supports regexes?」和「Resolves IP addresses?」两行;后者把 Go 标为「Yes」,但文章正文自己说只有 Ruby 会解析主机名,而 Go 1.27.1 的源码也不会。Ruby 和 Java 未经测试。

它的最小公分母建议只有一部分仍然成立。只用小写的 no_proxy、逗号和精确 IPv4 地址在全部 15 个客户端中都有效;前导点如文章所警告的那样在 4 个客户端中仍不匹配 apex 域名;只有 4 个客户端遵守 IPv4 CIDR,所以避免 CIDR 仍然是合理的。「后缀总是会被匹配」在 Node 的 http 客户端中不成立,「以逗号分隔的 hostname:port 值」每个条目只在 9 到 11 个客户端中匹配(8 个客户端三个条目都匹配),而没有任何一种 IPv6 形式取得一致。

其中几种行为是最近才出现的。Requests 2.34.0(2026 年 5 月 11 日)加入了标签边界。httpx2 2.5.0(2026 年 6 月 25 日)不再因 IPv6 CIDR 条目崩溃,但也不匹配它们。undici 8.10.0、8.10.1 和 8.11.0(2026 年 8 月和 9 月)加入了不带方括号的 IPv6、尾随点和新的通配符规则。

测试如何进行,以及如何重新运行

一个位于 127.0.0.1 的小型 HTTP 代理记录了每个绝对形式的请求和每个 CONNECT,自行应答,不转发任何内容。每个请求都在 macOS sandbox-exec 下的独立进程中运行,其配置文件拒绝除回环之外的所有出站流量,因此直连尝试在本机就会失败,不会到达任何真实主机。只有当代理记录到其请求时,一个单元格才计为经代理;客户端是否得到响应从不影响判定。不设置 NO_PROXY 时,全部 15 个客户端对每种 URL 形式都走了代理。

已发布的运行于 2026 年 9 月 23 日 19:13 至 19:15 UTC 进行,共有 1,266 个单元格。一次从已发布归档按下面步骤进行的干净重跑,对全部 1,266 个单元格给出了完全相同的分类。

要在不运行任何测试的情况下阅读已发布的矩阵,解压归档并运行 python3 no_proxy_lab.py table results.json。它不需要任何安装或网络,而且在 Python 3.9.6 和 3.14.7 上打印的输出相同。完整重跑需要 macOS、Python 3.14 和 Go、约 700 MB 磁盘空间,以及在 setup 期间访问 nodejs.org、pypi.org 和 registry.npmjs.org;setup 不会在系统范围内安装任何东西:

sh
curl -fLO https://ipvolt.com/downloads/no-proxy-matching-tested/no-proxy-matching-tested.zip
unzip no-proxy-matching-tested.zip
cd no-proxy-matching-tested
python3.14 no_proxy_lab.py setup --work ./work
python3.14 no_proxy_lab.py run --work ./work --out ./out
python3.14 no_proxy_lab.py compare results.json out/results.json
python3.14 no_proxy_lab.py table out/results.json --section main

只有当每个单元格的分类都相同时,compare 才以 0 退出。README 解释了使用其他客户端构建时它的输出、如何测试其他版本或你自己的 NO_PROXY 值、面向其他系统的 --isolation none 模式,以及 results.json 的每个字段。

下载:

局限

  • 一台 macOS 15.7.4 arm64 机器、一个日期和上述版本。任何一次发布都可能改变某一行:Node 26.10.0 尚未捆绑 undici 8.11.0,而 Go 1.28 预计将改变变量冲突那一行。
  • 未测试 Linux。每个客户端都在自己的代码或其语言的标准库中匹配 NO_PROXY,而在设置了代理变量的情况下,Python 的 urllib 在 macOS 上走的是与 Linux 相同的环境路径。因此在 Linux 上,各行应当取决于客户端版本而不是操作系统。这是从源码得出的推断,不是 Linux 上的结果;README 给出了示例。
  • 矩阵只测试了环境变量的处理(采用上面列出的设置),并且只测试了 HTTP 正向代理。它没有测试显式代理选项、其他 agent 或 dispatcher、系统代理设置、Windows、SOCKS、PAC 文件、重定向或代理认证。关于 407 响应,见如何修复代理错误 407
  • 未测试连字符范围、192.168.* 这样的部分地址、依赖 DNS 解析的条目,以及 Ruby、Java、.NET、Deno 和 Bun;README 列出了其余未测试项。
  • 上文的两次一次性检查于 2026 年 9 月 24 日使用相同的测试框架和客户端运行。它们不在 results.json 中;README 说明了如何重新运行它们。

ipvolt 于 2026 年 9 月 23 日和 24 日使用上面列出的客户端版本在本地运行了这些检查。没有测试任何外部服务商、代理服务或网络,本文内容也不描述 ipvolt 自己的服务。

如果你想在 ipvolt 开放访问时收到通知,请加入早期访问名单。访问开放时发送一封邮件。仅此而已。

参考来源

  1. GitLab (2021): We need to talk: Can we standardize NO_PROXY?
  2. nodejs/node#57872: node:http vs fetch() NO_PROXY table (comment, 2026-09-01)
  3. nodejs/node#65616: NO_PROXY=example.com and http.request() subdomains
  4. nodejs/node#65617: http: match subdomains for plain NO_PROXY entries
  5. nodejs/node#66202: fetch() and http.request() with an empty lowercase variable
  6. Node.js 26.10.0 docs: NO_PROXY format
  7. Node.js 26.10.0 docs: NODE_USE_ENV_PROXY
  8. Node.js 26.10.0 source: lib/internal/http.js (http.request matcher)
  9. Node.js 26.10.0: bundled undici version
  10. undici PR #5777: NO_PROXY wildcard semantics
  11. undici v8.11.0 release notes
  12. undici PR #5623: match bare IPv6 addresses in no_proxy
  13. undici PR #5637: ignore trailing dots when matching no_proxy
  14. undici v8.11.0 docs: ProxyAgent
  15. undici v8.11.0 docs: EnvHttpProxyAgent
  16. libcurl: CURLOPT_NOPROXY
  17. curl changelog
  18. curl#19828: IPv6 CIDR notation in NO_PROXY (fixed in 8.18.0)
  19. Go: golang.org/x/net/http/httpproxy Config
  20. Go: httpproxy Config.ProxyFunc (localhost and loopback)
  21. golang.org/x/net commit a02ddfa7ea: httpproxy prefers lowercase proxy variables (x/net v0.58.0)
  22. golang/go#79656: inconsistent HTTP_PROXY precedence (milestone Go 1.28)
  23. Go 1.28 release-note draft: ProxyFromEnvironment prefers lowercase variables
  24. GNU Wget manual: Proxies
  25. Python 3.14 docs: urllib.request
  26. CPython 3.14.7 source: Lib/urllib/request.py (proxy_bypass by platform)
  27. Requests v2.34.0 release notes
  28. Requests v2.34.2 source: utils.py (should_bypass_proxies)
  29. Requests PR #7586: honor ports in IPv4 no_proxy entries
  30. httpx2 changelog (v2.13.1)
  31. httpx2 PR #1165: support IP CIDR ranges in NO_PROXY
  32. aiohttp docs: proxy support and trust_env
  33. aiohttp v3.14.3 source: helpers.py

标签:ProxiesTroubleshooting