Node.js 中的 Axios 请求绕过代理,或者在经过代理时失败,原因就在下面这 16 个之中,而且大多数与 Axios 版本有关。Axios 1.21.0 和 1.21.1 在设置了 proxy: false 时会忽略带 proxyEnv 的 agent。httpVersion: 2 和 fetch 适配器在所有支持它们的版本上都是直连。1.16.1 之前的版本从不为 https:// 地址建立 CONNECT 隧道。每个原因都附有我们记录的输出和我们实际运行过的修复方法。
测试环境:Node.js 24.22.2 和 26.13.0,Axios 1.12.2、1.19.0、1.20.0、1.21.0 和 1.21.1,另加 1.13.6、1.15.0、1.16.0、1.16.1 和 1.18.1 用来定位行为变化的版本,https-proxy-agent 9.1.0 和 socks-proxy-agent 10.1.0,macOS 26.5.1 arm64,2026 年 11 月 11 日。实验环境是 127.0.0.1 上一个带日志的代理和一个 HTTPS 目标。“被绕过”指目标作出了响应,而代理什么也没有记录。脚本、结果表和全部 1290 条记录都在实验文件夹中(英文)。第一次配置代理,请先看 Axios 代理设置;本页针对的是配置好了却表现不对的情况。
快速检查:请求是否经过代理?
import axios from 'axios';
// Use the same client and options as the code you are debugging.
const res = await axios.get('https://api.ipify.org?format=json', {
proxy: { protocol: 'http', host: 'proxy.example', port: 8080 },
});
console.log(res.data);用出问题的那段代码的选项运行一次,再完全不带代理运行一次。如果两次输出的 IP 相同,说明请求绕过了代理。200 什么也证明不了:本页的每一次绕过都返回了 200。如果能看到代理自己的日志,先看那里。在实验中,每个经过代理的 https:// 请求都留下一行 CONNECT,被绕过的请求一行也没有。
找到你的症状
| 你看到的现象 | 前往 |
|---|---|
状态 200,但 IP 是你自己的,代理没有任何日志 | 请求绕过代理 |
400、socket hang up、wrong version number、ENOTFOUND undefined,或请求一直不返回 | 经 HTTP 代理访问 HTTPS 失败 |
Request failed with status code 407 | 407 与认证错误 |
设置了 HTTPS_PROXY 却被忽略,或不该跳过代理的主机被跳过 | 环境变量与 NO_PROXY |
| 第一个请求走代理,重定向却没有 | 重定向离开代理 |
protocol mismatch,或 SOCKS 代理无法解析主机名 | SOCKS5 代理 |
请求绕过代理,且没有任何报错
1. proxy: false 与带 proxyEnv 的 agent 同时使用(Axios 1.21.0 及之后)
import https from 'node:https';
import axios from 'axios';
const agent = new https.Agent({ proxyEnv: { https_proxy: 'http://proxy.example:8080' } });
const res = await axios.get('https://example.com/', { proxy: false, httpsAgent: agent });
console.log(res.status);在 Axios 1.20.0 上,这个请求经过了实验代理。在 1.21.0 上,同一个脚本输出:
200
# proxy log: (nothing)
# target log: GET /ip HTTP/1.1 direct从 1.21.0 起,proxy: false 的含义是“直接连接”,即使 agent 自带代理设置也一样:Axios 会把 agent 换成一个不带 proxyEnv 的副本(#11402,2026 年 11 月 5 日关闭;改动来自 PR #11242,提交 2498c23)。在实验中,Node 自带的开关也是同样的结果:NODE_USE_ENV_PROXY=1 或 --use-env-proxy 加上 proxy: false,在 1.20.0 及之前走代理,在 1.21.0 和 1.21.1 上直连。
修复:从使用这类 agent 的请求中去掉 proxy: false。#11402 的报告者确认了这一点,在实验中去掉该选项的脚本在全部十个版本上都经过了代理。agent 带有 proxyEnv 时,Axios 会把环境代理交给它处理(请求配置文档)。与 HttpsProxyAgent 这类第三方 agent 搭配时,请保留 proxy: false:这一组合在所有版本上都有效(原因 5)。
2. httpVersion: 2
import axios from 'axios';
// HTTPS_PROXY=http://proxy.example:8080 is set in the environment.
const res = await axios.get('https://example.com/', { httpVersion: 2 });
console.log(res.status);在 Axios 1.21.1 上:
200
# proxy log: (nothing)
# target log: GET /ip HTTP/2.0 direct| Axios | proxy 对象 | HTTPS_PROXY | proxy: false + HttpsProxyAgent |
|---|---|---|---|
| 1.13.6、1.15.0、1.16.0 | ERR_HTTP2_ERROR | ERR_HTTP2_ERROR | 被绕过,无报错 |
| 1.16.1、1.18.1、1.19.0 | 被绕过,无报错 | 被绕过,无报错 | 被绕过,无报错 |
| 1.20.0、1.21.0、1.21.1 | ERR_NOT_SUPPORT | 被绕过,无报错 | 被绕过,无报错 |
Axios 1.12.2 不支持 HTTP/2,忽略了这个选项。安全公告 GHSA-3pq3-5fj3-cg6v(CVE-2026-101898,GitLab 条目)覆盖 1.13.0 至 1.20.0 之前的版本:HTTP/2 路径自行建立连接,丢掉了代理。实验在 1.16.1 到 1.19.0 上复现了无声的绕过。在 1.13.6、1.15.0 和 1.16.0 上,请求向代理发送了 HTTP/2 字节后以 Protocol error 失败,所以在这些版本上失败是看得见的,而不是无声的。
1.20.0 的修复会拒绝显式的 proxy 对象,报错 HTTP/2 requests with a proxy are not supported。它不覆盖环境变量:PR #11141 写明,当代理只来自环境变量或 agent 时,HTTP/2 仍然直连;文档也说 HTTP/2 请求无法走代理。NODE_USE_ENV_PROXY=1 同样是直连。
修复:凡是必须走代理的请求,都去掉 httpVersion: 2。去掉后,同一个请求在 1.16.1 及之后的版本上以 HTTP/1.1 经过了代理。
3. fetch 适配器
使用 adapter: 'fetch' 时,proxy 对象、HTTPS_PROXY 和 httpsAgent 在全部十个版本、两条 Node 版本线上都被无声绕过。文档把 proxy 标为仅限 Node.js,而在实验中只有默认的 HTTP 适配器会读取该选项、代理环境变量或 agent。
修复:去掉 adapter: 'fetch',或者让 Node 为自己的 fetch 走代理:设置 NODE_USE_ENV_PROXY=1 和 HTTPS_PROXY 后,fetch 适配器在 Node 24.22.2 和 26.13.0 上都经过了代理。proxy: false 并不能关掉它。如果报错是 fetch failed,请看修复 Node.js 在代理后的 fetch failed 和 Node.js 26 上的 invalid onError method。
4. agent 放错了选项
只把 HttpsProxyAgent 作为 httpAgent 传入,而地址是 https://:在所有版本上都被无声绕过。Axios 对 http:// 请求使用 httpAgent,对 https:// 请求使用 httpsAgent(README)。
修复:两个选项都设置,像下文 SOCKS 示例那样。
经 HTTP 代理访问 HTTPS 失败
5. 1.16.1 之前的 Axios 不发送 CONNECT
使用 proxy 对象或 HTTPS_PROXY 时,Axios 1.12.2、1.13.6、1.15.0 和 1.16.0 没有向代理请求隧道。实验代理在一个普通请求里收到了完整的 URL:
AxiosError: Request failed with status code 400
# exit code: 1
# proxy log: A GET https://target.test:<T>/ip -> 400
# target log: (nothing)我们的代理用 400 拒绝这种请求。另一个会直接断开连接的实验代理则产生了 socket hang up(ECONNRESET),也就是 #4531 中报告的错误。该 issue 于 2022 年 3 月提交,2025 年 7 月 30 日在一次清理中关闭,当时要求报告者重新测试。#3384 于 2022 年 6 月关闭,描述的是同样的配置,但报的是某个代理特有的握手错误,我们未能复现。
Axios 1.16.1 改变了这一点:按照 PR #10858,Axios 对 HTTPS 目标使用 CONNECT 隧道,而不再把请求的 URL、请求头和请求体以明文转发给代理。从 1.16.1 起,这些用例全部走了隧道。
修复:升级到 1.16.1 或更高版本。如果无法升级,就使用代理 agent,并关闭 Axios 自己的代理处理。这在全部十个版本上都有效,包括 1.12.2:
import axios from 'axios';
import { HttpsProxyAgent } from 'https-proxy-agent';
const agent = new HttpsProxyAgent('http://proxy.example:8080');
const res = await axios.get('https://example.com/', {
proxy: false, // Axios adds no proxy handling of its own
httpsAgent: agent, // the agent opens the CONNECT tunnel
signal: AbortSignal.timeout(15000),
});
console.log(res.status);6. wrong version number:代理的协议写错或漏写
write EPROTO <id>:error:0A00010B:SSL routines:tls_validate_record_header:wrong version number:../deps/openssl/openssl/ssl/record/methods/tlsany_meth.c:78:客户端在用 TLS 与一个只讲明文 HTTP 的代理端口通信。在所有版本上,以下写法都让实验得到了这个错误:proxy 对象中的 protocol: 'https'、HTTPS_PROXY=https://127.0.0.1:8080、完全不带协议的 HTTPS_PROXY=127.0.0.1:8080,以及 new HttpsProxyAgent('https://…')。在 1.16.0 及之前,不带 protocol 的 proxy 对象也以同样方式失败;从 1.16.1 起它可以正常工作。
protocol 和 URL 的协议描述的是到代理的连接,而不是到网站的连接:只有代理本身提供 TLS 时才使用 https(README)。#6198 报告了使用 HttpsProxyAgent 时的这个错误;它在 2025 年 8 月因缺少复现步骤而关闭,而其中的配置换成 http:// 代理地址后在我们的实验中可以正常工作。
修复:对普通的 HTTP 代理,写 protocol: 'http',或在 URL 中写 http://。经代理出现 SSL: wrong version number 讲的是其他客户端中的同一个错误。
7. 代理写成了字符串,或协议写进了 host
proxy: 'http://proxy.example:8080'(字符串):从 1.16.1 起是getaddrinfo ENOTFOUND undefined,此前是connect ECONNREFUSED ::1:443。proxy: { host: 'http://proxy.example', port: 8080 }:从 1.16.1 起是Invalid URL(ERR_INVALID_URL),此前是getaddrinfo ENOTFOUND http://127.0.0.1。
两种写法都没有到达代理。2020 年关闭的 #2072 在浏览器代码中用了第二种写法,而 proxy 选项在浏览器中根本不起作用(文档);我们没有测试浏览器。
修复:传入一个对象,host 写不带协议的主机名,port 写数字或字符串,协议写在 protocol 中。
8. 请求一直不返回
从 Axios 1.16.1 起,使用 proxy 对象或 HTTPS_PROXY 时,如果代理在收到 CONNECT 后关闭连接,或者始终不应答,请求就会一直挂起:15 秒后既没有响应也没有错误,尽管设置了 timeout: 5000。在 1.16.1 之前,同样的用例会以 socket hang up 或 timeout of 5000ms exceeded 被拒绝。使用 proxy: false 加 HttpsProxyAgent 时,连接被关闭会以 Proxy connection ended before receiving CONNECT response 被拒绝,而不应答的代理在全部十个版本上都让请求一直挂起。http:// 地址则正常被拒绝,报 timeout of 5000ms exceeded 或 socket hang up。截至 2026 年 11 月 11 日,我们没有找到关于这个问题的 Axios issue。
修复:给每个走代理的请求单独设置截止时间。signal: AbortSignal.timeout(3000) 让每个挂起的用例在 3 秒后以 canceled(ERR_CANCELED)结束。maxRedirects: 0 也能让 timeout 对内置隧道生效,1.16.0 的发布说明提到过这种情况。
407 与认证错误
密码错误或没有密码时,所有版本返回的结果都一样,无论凭据来自 proxy.auth、HTTPS_PROXY 还是 HttpsProxyAgent 的 URL:Request failed with status code 407,其中 error.code 为 ERR_BAD_REQUEST,error.response.status 为 407。代理记录了一个请求并以 407 应答,目标什么也没有看到。SOCKS5 代理没有状态码:那里密码错误得到的是 Socks5 Authentication failed。
9. 凭据放错了位置
把代理的用户名和密码放在顶层的 auth 选项中,返回了 407,代理日志显示它没有收到任何凭据。顶层的 auth 是给网站用的;代理的凭据要放在 proxy.auth 中(README)。使用 proxy.auth 时,从 1.16.1 起代理对 CONNECT 完成了认证,目标始终没有看到 Proxy-Authorization 请求头,重定向到第二个主机时也一样。
10. 特殊字符的编码方式不对
实验代理期望的密码是 p@ss/w0rd#1:
| 密码写在哪里 | 写法 | 结果 |
|---|---|---|
proxy.auth.password | 原样 | 经过代理(1.16.1 及之后) |
proxy.auth.password | 百分号编码 | 407:代理收到的是 p%40ss%2Fw0rd%231 |
HTTPS_PROXY 的 URL | 原样 | getaddrinfo ENOTFOUND ss |
HTTPS_PROXY 的 URL | 百分号编码 | 407:代理收到的是 p%40ss%2Fw0rd%231 |
HttpsProxyAgent 的 URL | 百分号编码 | 经过代理(所有版本) |
原样粘贴时,@ 提前结束了凭据部分,ss 成了主机名。使用百分号编码时,URL 可以解析,但 Axios 把其中的用户名和密码原样取出,不做解码(1.21.1 的 http.js)。因此在测试过的版本上,含保留字符的密码根本无法通过 HTTPS_PROXY 传递。
修复:在 proxy.auth 中不编码地传入,或者在 agent 的 URL 中使用百分号编码。下面两个请求在 1.19.0 到 1.21.1 上都经过了代理:
import axios from 'axios';
import { HttpsProxyAgent } from 'https-proxy-agent';
const user = process.env.PROXY_USER;
const pass = process.env.PROXY_PASS;
// In the proxy object: the raw password, not percent-encoded.
const a = await axios.get('https://example.com/', {
proxy: { protocol: 'http', host: 'proxy.example', port: 8080, auth: { username: user, password: pass } },
});
// In a proxy URL for an agent: percent-encode both parts.
const url = `http://${encodeURIComponent(user)}:${encodeURIComponent(pass)}@proxy.example:8080`;
const b = await axios.get('https://example.com/', { proxy: false, httpsAgent: new HttpsProxyAgent(url) });
console.log(a.status, b.status);如果凭据正确仍然出现 407,例如由于 IP 白名单或用户名格式,请看修复代理错误 407。
环境变量没有生效,或 NO_ PROXY 匹配不对
在实验中,Axios 在全部十个版本上读取的是:https:// 地址读 HTTPS_PROXY 或 https_proxy,http:// 地址读 HTTP_PROXY 或 http_proxy,缺少对应变量时读 ALL_PROXY 或 all_proxy。大小写两种写法都设置时,小写的优先。变量在发出请求时读取,所以在请求前用代码给 process.env.HTTPS_PROXY 赋值是有效的。proxy 对象的优先级高于这些变量和 NO_PROXY。在 1.16.1 之前,变量的读取方式相同,但 https:// 请求随后会像原因 5 那样失败。
11. 变量与 URL 的协议不匹配
只设置 HTTP_PROXY 时,https:// 请求在所有版本上都是直连,没有任何报错。只设置 HTTPS_PROXY 时,http:// 请求也是如此。
修复:两个都设置,通常指向同一个 http:// 代理地址。代理环境变量比较了其他客户端读取它们的方式。
12. 环境中的代理覆盖了你的 agent
设置了 HTTPS_PROXY 时,作为 httpsAgent 传入、且没有 proxy: false 的 HttpsProxyAgent 或 SocksProxyAgent 会被忽略:从 1.16.1 起,请求走的是环境变量中的代理,而不是 agent 的代理。与 agent 同时存在的 proxy 对象也以同样的方式胜出。只要 CI 运行器或容器设置了 HTTPS_PROXY,你的流量就可能走另一个代理,而不是代码中写的那个。
修复:凡是使用第三方代理 agent 的请求,都加上 proxy: false。加上之后,无论环境里有什么,请求在全部十个版本上都走了 agent 的代理。
13. NO_PROXY 在不同版本上匹配结果不同
设置了代理变量,URL 的主机为 target.test 时:
NO_PROXY | 路由 | 版本 |
|---|---|---|
target.test、TARGET.TEST、.test、*.test、*、带 URL 端口的 target.test:PORT | 直连 | 全部 |
other.test target.test 或 other.test, target.test | 直连 | 全部 |
.target.test、test、get.test、target.test:9999、https://target.test | 代理 | 全部 |
target.test,而 URL 写的是带结尾点号的 target.test. | 1.12.2 和 1.13.6 走代理,1.15.0 起直连 | 在 1.15.0 中修复 |
127.0.0.0/8,URL 主机为 127.0.0.1 | 1.19.0 及之前走代理,1.20.0 起直连 | 在 1.20.0 中修复 |
localhost,URL 主机为 127.0.0.1 | 1.15.0 及之前走代理,1.16.0 起直连 | 在 1.16.0 中变更 |
结尾点号那一行对应 GHSA-3p68-rc4w-qgx5,在 1.15.0 中修复。CIDR 那一行对应 GHSA-44g4-m2mj-wpvx(CVE-2026-101899),在 1.20.0 中修复;公告列出的受影响版本是 1.15.0 及之后,而在实验中 1.12.2 和 1.13.6 同样忽略了 CIDR 条目。未设置 NO_PROXY 时,发往 localhost 和 127.0.0.1 的请求在所有版本上都走了代理。NO_PROXY 匹配规则实测中有针对其他客户端的同一张矩阵。
重定向离开代理
14. 重定向改变了协议,或跳到了 NO_PROXY 中的主机
实验目标以 302 跳转到第二个主机。
- 从
http://到https://,只设置了HTTP_PROXY:第一跳走代理,第二跳直连,所有版本都是如此。使用proxy: false且只设置httpAgent时也一样。两个 agent 都设置时,或从 1.16.1 起两个变量都设置时,两跳都走代理。 - 从
https://到另一个主机的https://:使用proxy对象、HTTPS_PROXY或 agent 时,从 1.16.1 起两跳都走代理。启用代理认证时,两个CONNECT请求都带有凭据。 - 重定向到
NO_PROXY中列出的主机:在 1.21.0 和 1.21.1 上,第二跳直连。在 1.16.1 到 1.20.0 上,它仍然走代理。使用显式的proxy对象时,从 1.16.1 起两跳都走代理。
修复:两个变量或两个 agent 都设置。如果重定向绝不能离开代理,就使用 proxy 对象,或一对 agent 加 proxy: false,并且不要把那个主机写进 NO_PROXY。
SOCKS5 代理
15. 在 proxy 选项或 HTTPS_PROXY 中使用 socks5://
Axios 没有 SOCKS 客户端。proxy: { protocol: 'socks5', … } 和 HTTPS_PROXY=socks5://… 在 1.16.0 及之前以 protocol mismatch(ERR_ASSERTION)失败。从 1.16.1 起,Axios 向 SOCKS 端口发送了一行 HTTP CONNECT,实验的 SOCKS 服务器关闭了连接,请求一直不返回(原因 8)。
修复:在两个 agent 选项中都使用 SOCKS agent,并加上 proxy: false(原因 12)。这样请求在全部十个版本上都经过了实验的 SOCKS5 代理:
import axios from 'axios';
import { SocksProxyAgent } from 'socks-proxy-agent';
// socks5h: the proxy resolves the hostname. socks5: this machine does.
const agent = new SocksProxyAgent('socks5h://proxy.example:1080');
const res = await axios.get('https://example.com/', {
proxy: false,
httpAgent: agent,
httpsAgent: agent,
signal: AbortSignal.timeout(15000),
});
console.log(res.status);16. 该用 socks5h 却用了 socks5
使用 socks5:// 时,实验的 SOCKS 服务器收到的是 IP 地址:客户端自己解析了主机名。使用 socks5h:// 时,它收到的是主机名(socks-proxy-agent README)。对于只有代理才能解析的名称,socks5:// 以 getaddrinfo ENOTFOUND proxy-only.test 失败,而 socks5h:// 可以正常工作。SOCKS5 与 SOCKS5h 说明了本地 DNS 查询还会泄露什么。
每种需求的可靠配置
三种配置在 Axios 1.19.0、1.20.0、1.21.0 和 1.21.1 上、两条 Node 版本线上都经过了实验代理。在 1.12.2 上只有第三种有效。
在代码中固定一个代理(Axios 1.16.1 或更高版本):
import axios from 'axios';
const client = axios.create({
proxy: {
protocol: 'http', // how to reach the proxy, not the site
host: 'proxy.example',
port: 8080,
auth: { username: process.env.PROXY_USER, password: process.env.PROXY_PASS },
},
});
const res = await client.get('https://example.com/', { signal: AbortSignal.timeout(15000) });
console.log(res.status);来自环境变量的公司代理(Axios 1.16.1 或更高版本)。在代码之外设置变量:
export HTTPS_PROXY=http://proxy.example:8080
export HTTP_PROXY=http://proxy.example:8080
export NO_PROXY=localhost,127.0.0.1import axios from 'axios';
// Started with HTTPS_PROXY, HTTP_PROXY and NO_PROXY in the environment.
// No proxy option, no agent, no httpVersion, default adapter.
const res = await axios.get('https://example.com/', { signal: AbortSignal.timeout(15000) });
console.log(res.status);每个请求使用不同的代理(所有测试过的版本):
import axios from 'axios';
import { HttpsProxyAgent } from 'https-proxy-agent';
const agents = new Map();
const agentFor = (proxyUrl) => {
if (!agents.has(proxyUrl)) agents.set(proxyUrl, new HttpsProxyAgent(proxyUrl));
return agents.get(proxyUrl);
};
const viaProxy = (url, proxyUrl) =>
axios.get(url, { proxy: false, httpsAgent: agentFor(proxyUrl), signal: AbortSignal.timeout(15000) });
const first = await viaProxy('https://example.com/', 'http://proxy-a.example:8080');
const second = await viaProxy('https://example.com/', 'http://proxy-b.example:8080');
console.log(first.status, second.status);在实验中,两个请求分别从两个不同的代理发出。对于 http:// 地址,再把 http-proxy-agent 的 HttpProxyAgent 作为 httpAgent 传入。
给测试加一道防护
上面的大多数原因都返回 200,所以当一次升级或一个新选项开始绕过代理时,普通的测试套件仍然全部通过。下面这个测试会启动一个替身代理,它记录收到的内容并予以拒绝,然后通过你的应用构建的客户端发送一个请求:
import assert from 'node:assert/strict';
import { once } from 'node:events';
import http from 'node:http';
import { test } from 'node:test';
import { createClient } from './client.mjs';
test('the HTTP client sends requests through the proxy', async () => {
const seen = [];
// A stand-in proxy: it records what arrives and refuses it, so nothing leaves the machine.
const proxy = http.createServer((req, res) => {
seen.push(`${req.method} ${req.url}`);
res.writeHead(503).end();
});
proxy.on('connect', (req, socket) => {
seen.push(`CONNECT ${req.url}`);
socket.end('HTTP/1.1 503 Service Unavailable\r\n\r\n');
});
proxy.listen(0, '127.0.0.1');
await once(proxy, 'listening');
const client = createClient(`http://127.0.0.1:${proxy.address().port}`);
// .invalid never resolves, so a request that skips the proxy cannot reach a real site.
await client.get('https://guard.invalid/', { signal: AbortSignal.timeout(5000) }).catch(() => {});
proxy.close();
assert.deepEqual(seen, ['CONNECT guard.invalid:443']);
});createClient(proxyUrl) 是你自己的工厂函数。使用上面“每个请求使用不同的代理”的客户端时,node --test 在五个主要版本上全部通过。给同一个客户端加上 adapter: 'fetch' 之后,它失败了:
not ok 1 - the HTTP client sends requests through the proxy
Expected values to be strictly deep-equal:
+ actual - expected
+ []
- [
- 'CONNECT guard.invalid:443'
- ]
# tests 1
# pass 0
# fail 1
# exit code: 1该用和该避开的版本
- 使用 1.21.1,即测试当天的最新版本,或至少使用 1.20.0。升级后检查两件事:
proxy: false与带proxyEnv的 agent 同时使用(原因 1),以及重定向到NO_PROXY中的主机(原因 14)。 - 1.16.1 之前:经 HTTP 代理访问
https://地址时没有 CONNECT 隧道(原因 5)。请使用 agent 方案或升级。 - 1.13.0 到 1.19.0 且使用
httpVersion: 2:在 GHSA-3pq3-5fj3-cg6v 的范围内。在 1.20.0 及之后,HTTP/2 仍然忽略HTTPS_PROXY(原因 2)。 - 1.15.0 之前和 1.20.0 之前:
NO_PROXY分别无法匹配带结尾点号的主机和 CIDR 网段(原因 13)。 - Node.js:24.22.2 和 26.13.0 对每个用例的路由结果相同。唯一的区别:通过 fetch 适配器时,Node 26 与实验目标协商了 HTTP/2,而 Node 24 使用 HTTP/1.1。
常见问题
为什么 Axios 忽略 HTTPS_PROXY?
在实验中有六种原因:请求设置了 proxy: false、httpVersion: 2、adapter: 'fetch'、优先级更高的 proxy 对象、命中了 NO_PROXY,或者地址是 http://,这时读取的是 HTTP_PROXY。
使用 Axios 还需要 https-proxy-agent 吗?
在 Axios 1.16.1 或更高版本上使用单个 HTTP 代理时不需要:proxy 对象和 HTTPS_PROXY 会自行建立隧道。旧版本需要它,而且它是为每个请求使用不同代理的最简单方式。
Axios 支持 SOCKS5 代理吗?
不能通过 proxy 或 HTTPS_PROXY 使用。请把 SocksProxyAgent 作为 httpAgent 和 httpsAgent 传入,并加上 proxy: false。
为什么设置了 timeout,Axios 请求经过代理时还是会挂起?
在测试过的版本上,timeout 不覆盖代理不应答 CONNECT 的情况(原因 8)。请加上 signal: AbortSignal.timeout(ms)。
Axios 的 proxy 选项在浏览器中有效吗?
无效。文档把 proxy 标为仅限 Node.js;浏览器使用操作系统或浏览器自身的代理。
相关指南
- Axios 代理设置是配置页:一次显式的代理检查,以及由哪一层决定路由。
- 代理环境变量比较了 curl、Requests 和 Node 读取它们的方式。
- 修复代理错误 407 讲的是与编码无关的 407。
- fetch failed:Node.js 26 上的 invalid onError method 是 undici 代理 agent 在 Node 26 上的故障。
- 修复 Node.js 在代理后的 TypeError: fetch failed 讲的是使用
fetch时如何读错误链。 - NO_PROXY 匹配规则实测中有其他客户端的匹配规则。
用真实 IP 测试这个修复。 ipvolt 出售住宅代理,每 GB $2.20 起,以及专用的移动代理,每天 $9 起。把代理填入上面的配置,然后运行快速检查。立即检查代理。
参考来源与延伸阅读
本指南参考的技术资料。请以你所安装版本的文档以及代理服务商支持的配置为准。
- axios/axios#11402: axios@1.21.0 breaks custom https.Agent with proxyEnv enabled
- axios/axios#11242: align request handling across runtimes (direct agents for proxy: false)
- axios/axios commit 2498c23
- GHSA-3pq3-5fj3-cg6v (CVE-2026-101898): HTTP/2 adapter bypasses configured DNS lookup and proxy controls
- GitLab advisory database: CVE-2026-101898
- axios/axios#11141: HTTP/2 proxy handling and CIDR matching for NO_PROXY
- GHSA-3p68-rc4w-qgx5: NO_PROXY hostname normalization bypass
- GHSA-44g4-m2mj-wpvx (CVE-2026-101899): CIDR-form NO_PROXY entries are ignored
- axios/axios#4531: Proxy support does not work properly with CONNECT on https requests
- axios/axios#3384: HTTPS over HTTP Proxy Fails
- axios/axios#2072: Axios proxy is not working
- axios/axios#6198: https proxy agent not working as expected
- axios/axios#10858: CONNECT tunnelling for HTTPS targets
- Axios 1.16.0 release notes
- Axios 1.16.1 release notes
- Axios 1.20.0 release notes
- Axios 1.21.0 release notes
- Axios 1.21.1 release notes
- Axios docs: request config (proxy, httpAgent, httpsAgent, environment variables)
- Axios 1.21.1 README: request config
- Axios 1.21.1: HTTP adapter source
- Node.js HTTP: built-in proxy support and the Agent proxyEnv option
- Node.js CLI: NODE_USE_ENV_PROXY=1
- Node.js CLI: --use-env-proxy
- socks-proxy-agent README