如何为 Node.js 配置 Axios 代理(没错,HTTPS 也可以)

最后更新于 August 11, 2026
Axios request travelling through a CONNECT proxy tunnel to an HTTPS origin
AI 摘要
- 使用显式代理配置、环境变量以及自定义 HTTP/HTTPS agent,在 Axios 和 Node.js 中实现代理;这些都是 Axios 不会自动替你处理的场景。 - 理解 HTTPS CONNECT 隧道、代理 URL 与目标 URL 的区别,以及为什么 SOCKS 代理必须使用 agent,而不是标准 proxy 选项。 - 通过有上限的重试、轮询选择、健康评分、冷却机制和按请求超时,实现代理池管理,同时避免重试风暴。 - 使用针对传输层、代理层和源站层的定点检查,诊断 ECONNRESET、ETIMEDOUT、407、TLS、DNS 以及环境变量优先级问题。 - 将凭据排除在源码之外,并在生产自动化中让必需的代理路由以 fail closed 的方式运行。

现在的 Stack Overflow 上,总有人坚信 Axios 在 HTTPS 代理上会“悄悄失灵”。这类说法在 Node.js 代理教程里反复出现,但它并不适用于本指南测试的当前版本。我搭了一个本地测试环境,把真实的 HTTP 和 HTTPS 源站分别放在两个代理后面,结果 Axios 1.19.0 对 HTTPS 请求走的是标准的 CONNECT 隧道,而不是绕过代理。

这并不代表大家提到的痛点是假的。旧版 Axios 确实存在真实 bug(可查看 issue #3384issue #4531),而较新的 Node 版本又引入了另一条环境代理路径,需要更谨慎地配置。本指南会带你看清:截至今天,Axios 1.19.0 到底哪些用法是真的可用、怎样把代理接入请求、一个几乎没人讲过的拦截器轮换方案,以及当事情还是出错时,如何对照错误和修复方法一条条排查。

什么是 Axios 代理?为什么它在 Node.js 里很重要?

在 Axios 的语境里,代理就是夹在你的 Node 进程和目标网站之间的中转服务器。你的请求先发给代理,再由代理转发出去,目标站看到的是代理的 IP,而不是你的 IP。原理就这么简单。

开发者用它通常有几种原因:抓取有 IP 限流或封锁的网站、测试应用在不同地区的表现、让流量经过公司的出口网关,或者单纯不想让自己的服务器 IP 出现在某些目标站的访问日志里。Axios 官方请求配置 提供了内置的 proxy 选项,包含 hostportprotocolauth 字段——这个功能已经很多年了,也是所有教程(当然也包括本文)最先会讲的内容。

但经常被忽略的一点是:proxy 这个选项在访问 HTTP 目标和 HTTPS 目标时表现并不一样,还会受到 Axios 版本的影响。也正因为这个差异,这篇指南才有存在的意义。

配置 Node.js 和 Axios(快速准备)

如果你已经有项目了,可以跳过这一段。否则,整个过程大概两分钟就能搞定。

mkdir axios-proxy-demo && cd axios-proxy-demo
npm init -y
npm install axios

如果你想用 ESM import,就在 package.json 里加上 "type": "module"(我个人会这么做——用 CommonJS 的 require() 来演示代理,多少有点旧了)。当前 Node LTS 是 v24.18.0,不过我特意用 v22.22.3 做了测试,这样结果就不会受最新运行时细节的影响。

把下面代码放进 app.js,然后执行 node app.js

import axios from 'axios';

const res = await axios.get('https://httpbin.org/ip');
console.log(res.data);

你应该会在返回结果里看到自己的真实 IP。这就是基线——等代理配置成功后,同样的请求应该返回代理的 IP。

在启用代理之前先记下这个结果,这样你就能在下一步里把代理后的请求和基线进行对比。

Axios 真的支持 HTTPS 代理吗?先把结论说清楚

简短答案:支持,在当前稳定版里就是支持的。Axios 1.19.0 的文档明确说明了:当 HTTPS 目标站位于 HTTP 代理之后时,会通过 CONNECT 建立隧道。npm 下载 API 记录到 2026 年 7 月 31 日到 2026 年 8 月 6 日之间 Axios 的下载量为 117,890,039 次,这虽然只是一个过时的热度指标,但也说明这个库使用得非常广泛。你通过代理访问 HTTPS URL 时,当前 Axios 会发送 CONNECT 请求来建立隧道,TLS 握手则会端到端地在客户端和真实源站之间完成。我直接做了测试:本地 HTTP 代理、本地 HTTPS 源站(自签名证书),代理上的 CONNECT 计数器确实按预期增加了。

那为什么几乎所有论坛里都会出现“Axios 的 HTTPS 代理坏了”这种说法?原因有几个,而且都是真实存在的:

  • 老版本 Axios。 大家常贴出来的 GitHub issue 往往已经是好多年前的内容,描述的是特定版本、特定配置下的行为,不能直接套到现在的 Axios 上。
  • 代理服务器不支持 CONNECT。 在这种配置下,隧道建立会失败,Axios 应该直接报错;在判断是不是 IP 被绕过之前,先把实际路由和错误信息抓出来。
  • proxy 配置理解错了。 proxy 选项是“前向代理”指令,不是一个泛用的“无论如何都帮我把所有流量走这个代理”的开关。

Chromium 在 2023 年报告称,主流平台上超过 90% 的 Chrome 导航都使用了 HTTPS。这是一项过时的 Chrome 统计,不是整个 Web 的实时普查,但它足以说明为什么 HTTPS 目标行为应该是本教程的核心。如果你还在用很老的 Axios 版本,先在当前版本线上复现问题,再判断是不是历史遗留 bug;在你自己的应用里先做升级验证,再决定是否上线。

什么时候你仍然需要显式的 Agent

原生 proxy 配置适合单个、固定、常规的代理。但一旦你需要按请求控制、代理轮换,或者 SOCKS 支持,它就不够用了——Axios 的内置选项本来就不是为这些场景设计的。这时候就轮到 HttpsProxyAgent 出场了,下面我会详细说明。可以把原生选项理解为“一个代理、一个用途,够用就行”,而基于 agent 的方案则更像“生产环境里真正想要的那种控制力”。

Node v24 和 v22.21+ 的环境代理路径

较新的 Node 版本内置了环境代理模式,可通过 NODE_USE_ENV_PROXY=1--use-env-proxy 标志启用。根据 Node 官方 CLI 文档,这个能力从 v24.0.0 开始提供,并回移植到了 v22.21.0,所以说“Node 22+”并不准确,严格来说是 v22.21.0 及之后的版本。如果你还在更早的 Node 22 补丁版本里,这个标志根本不存在。

当前 Axios 已经通过它的 proxy-from-env 依赖解析 HTTP_PROXYHTTPS_PROXYNO_PROXY,所以在这条路径下并不需要 global-agent。如果同时启用了 Node 自己的环境代理模式,路由决策就可能涉及两层机制。

Axios 文档提到,在那些代理对象带有 proxyEnv 属性的 Node 版本上,Axios 会把代理解析交给 Node 自己处理,而不是走自己的逻辑。实际使用时,最好只选一种系统,并始终保持一致:

  • 交给 Node 处理: 打开标志,不配置 Axios 的 proxy,让环境变量负责路由。
  • 交给 Axios 处理: 不启用 Node 标志,让 Axios 自己读取环境变量。
  • 完全手动控制: 明确设置 proxy: false,再提供自己的 httpsAgent——这样可以完全绕开自动代理系统;一旦你需要轮换或按请求控制,我建议直接用这种方式。

我直接测试了 Axios 侧的解析:在子进程环境中设置 HTTP_PROXY 后,请求确实走了我的本地代理;再加上匹配的 NO_PROXY,下一次请求也正确地绕过了代理。也就是说,环境变量这条路现在确实开箱即用——真正需要警惕的是“双模式同时生效”的场景。

5 种把代理接入 Axios 的方法(对比)

在进入代码之前,先看看整体方案。我把下面每一种方式都接到真实的本地代理环境里做过测试,不只是看文档而已。

方法HTTPS 支持支持认证可按请求控制适合轮换复杂度
内联 proxy 选项✅(当前 Axios)
axios.create() 默认配置✅(当前 Axios)❌(实例级)
环境变量(HTTP_PROXY/HTTPS_PROXY
httpsAgent + HttpsProxyAgent⚠️(手动)
请求拦截器 + 代理池中高

如果你只是写一个脚本,且只需要走一个代理,就用内联选项。如果同一个模块里的所有请求都应该走同一个代理,并且你不想每次重复写配置,就用 axios.create()。如果你的基础设施团队已经在统一管理代理路由,而你只想继承环境变量,就直接用环境变量。如果原生配置不能满足你的控制需求,就上显式 agent;而当“控制”变成“轮换”时,就应该马上切到拦截器方案。

三种 Axios 代理路由方式汇聚到 HTTPS 目标站

逐步操作:Axios 的基础代理配置

最简单的方式,是直接在请求里使用内置的 proxy 对象:

import axios from 'axios';

const res = await axios.get('https://httpbin.org/ip', {
  proxy: {
    host: '203.0.113.10',
    port: 8080,
    protocol: 'http',
  },
});

console.log(res.data);

运行后,你应该会在响应里看到代理的 IP,而不是自己的 IP。如果你是在本地用真实代理做测试,通常不到一秒就能跑通;相比之下,光是为了测一个请求就手动去改系统级代理设置,往往能白白浪费十五分钟——而你根本不想把时间耗在这种事上。

把这个结果和前面的基线对比一下。成功的测试应该显示代理的公网 IP,而不是你之前记录的源站 IP。

使用 axios.create() 设置实例级默认值

如果某个模块里的所有请求都应该走同一个代理,那就把配置写进实例里,而不是每次都重复:

const client = axios.create({
  proxy: {
    host: '203.0.113.10',
    port: 8080,
  },
  timeout: 15_000,
});

const res = await client.get('https://httpbin.org/ip');

我验证过:对单个请求设置 proxy: false,可以干净地绕开实例默认代理——这在 95% 的请求都需要代理、但少数请求(比如健康检查)不需要代理时非常有用。

通过环境变量设置代理

如果你的路由是集中管理的,比如 Docker 容器或 CI 环境里运维团队已经帮你设置好了代理变量,那你甚至不需要碰 Axios 配置:

export HTTP_PROXY=http://203.0.113.10:8080
export HTTPS_PROXY=http://203.0.113.10:8080
export NO_PROXY=localhost,127.0.0.1

当前 Axios 会读取这些变量,而且不需要 global-agent。不过别忘了前面提到的 Node 版本边界:如果 NODE_USE_ENV_PROXY 也启用了,最好明确到底谁负责路由,并在实际部署运行时验证 NO_PROXY 的效果。

逐步操作:使用 httpsAgent 配置 HTTPS 代理(适合真正需要控制的时候)

如果你的需求已经不只是“固定一个代理”,那我更推荐这一套。先安装当前的 agent 包:

npm install https-proxy-agent

https-proxy-agent 9.1.0 需要 Node 20 或更高版本,并且会先向代理发出标准的 CONNECT,再通过隧道把目标连接转发出去。

import axios from 'axios';
import { HttpsProxyAgent } from 'https-proxy-agent';

const agent = new HttpsProxyAgent('http://203.0.113.10:8080');

const client = axios.create({
  proxy: false,        // 阻止 Axios 的原生解析再插一手
  httpsAgent: agent,
  timeout: 15_000,
});

const res = await client.get('https://httpbin.org/ip');
console.log(res.data);

当显式 agent 已经接管路由时,请设置 proxy: false。这样配置最清楚,也能避免 Axios 的原生代理解析或环境代理解析和你提供的 agent 发生冲突。

添加代理认证

把凭据直接放进代理 URL 里即可:

const agent = new HttpsProxyAgent('http://myuser:mypassword@203.0.113.10:8080');

如果密码里有特殊字符——@:# 最容易出问题——在拼接 URL 之前先做百分号编码,或者对每个部分分别用 encodeURIComponent()。密码里如果直接出现原始 @,解析器会把它当成主机名部分的开头,最后你会看到一个看似和编码无关、其实完全是编码导致的连接错误。

在 Axios 里使用 SOCKS5 代理

SOCKS 代理不能和 HttpsProxyAgent 直接兼容——这个协议需要另外的 agent:

npm install socks-proxy-agent
import { SocksProxyAgent } from 'socks-proxy-agent';

const agent = new SocksProxyAgent('socks5://myuser:mypass@203.0.113.10:1080');

const client = axios.create({
  proxy: false,
  httpsAgent: agent,
});

socks-proxy-agent 10.1.0 同样需要 Node 20+。当你面对的是只提供 SOCKS 网关的企业网络,或者代理服务商提供的协议能力比普通 HTTP 代理更灵活时,SOCKS5 就很值得用。

用 Axios 请求拦截器实现代理轮换

在调用代码里随机挑一个代理,做个一次性脚本还行。一旦请求量上来,比如几百次请求,这种写法就扛不住了:没人统一记录哪些代理已经挂了,没有重试逻辑,代理选择代码还会到处复制粘贴。Axios 的拦截器系统 能把这套逻辑收进一个可测试的地方;我在本文 SERP 里看过的五篇竞品教程,没有一篇用了这种方式。

Axios 的 GET 请求在三个代理之间轮换,并带有一次受限重试

构建代理池

import axios, { AxiosError, InternalAxiosRequestConfig } from 'axios';
import { HttpsProxyAgent } from 'https-proxy-agent';

class ProxyPool {
  private agents: HttpsProxyAgent<string>[];
  private index = 0;

  constructor(proxyUrls: string[]) {
    this.agents = proxyUrls.map((url) => new HttpsProxyAgent(url));
  }

  next(): HttpsProxyAgent<string> {
    const agent = this.agents[this.index];
    this.index = (this.index + 1) % this.agents.length;
    return agent;
  }
}

const pool = new ProxyPool([
  'http://user:pass@proxy1.example.com:8080',
  'http://user:pass@proxy2.example.com:8080',
]);

const client = axios.create({ timeout: 15_000 });

client.interceptors.request.use((config: InternalAxiosRequestConfig) => {
  config.proxy = false;
  config.httpsAgent = pool.next();
  return config;
});

我把这段代码接到两个本地代理上跑过,确认请求确实会交替发送——代理 A、代理 B、然后再回到 A。注意,Axios 的请求拦截器执行顺序是后进先出,所以如果你还有别的拦截器(比如加认证头、做日志),顺序比你想象中更重要。

再加一个带重试保护的响应拦截器

这就是大多数自己写轮换脚本时最容易写乱的地方。对所有失败都无脑重试,而且还用无限代理池,可能会把一次错误请求放大成连锁问题——尤其是像 POST 这种非幂等方法,重试可能会重复触发你根本不想重复的副作用。

type RetryableConfig = InternalAxiosRequestConfig & {
  __proxyRetryCount?: number;
};

client.interceptors.response.use(
  undefined,
  async (error: AxiosError) => {
    const config = error.config as RetryableConfig | undefined;
    if (!config) throw error;

    const method = String(config.method ?? 'get').toUpperCase();
    config.__proxyRetryCount ??= 0;
    if (method !== 'GET' || config.__proxyRetryCount >= 1) throw error;

    config.__proxyRetryCount += 1;
    config.proxy = false;
    config.httpsAgent = pool.next();
    return client.request(config);
  }
);

我用一个故意坏掉的代理测试过这段逻辑,确认只会在备用代理上重试一次——不会无限循环,也不会对 POST 请求重试。这才是你真正需要的边界:重试策略必须清楚知道哪些请求可以安全重放,而不是那种“先再试试,总会成功”的土办法。

Axios 代理故障排查:cURL、407 认证错误和超时检查

错误诊断表:把每一种失败都对应到解决办法

建议你收藏这一段。这里列的都是 Axios GitHub issue 和 Stack Overflow 里真实会出现的错误,而不是假设出来的。

错误 / 现象可能原因解决方法
ECONNREFUSED主机或端口写错,或者代理服务已停止先用 curl -x http://host:port target-url 验证,再去改 Axios 代码
407 Proxy Authentication Required凭据缺失或错误proxy 配置里加入 auth: { username, password },或者把凭据直接写进 HttpsProxyAgent 的 URL
403 Forbidden源站或 WAF 拒绝了请求,或者代理 IP 被拦检查网站访问策略、认证和请求频率;不要把换个请求头或换个 IP 当成绕过限制的借口
返回结果里显示的是你的真实 IPNO_PROXYproxy:false、显式直连 agent,或者某个历史/特定版本配置正在绕过代理检查到底是哪一层在负责路由;必要时用受控 IP 端点和显式 agent 验证路径
ETIMEDOUT连接或响应耗时超过了配置的超时时间先测量时间到底花在哪;只有在业务确实需要时才调大超时,否则就换路由或让不健康的代理冷却一下
响应过程中出现 ECONNRESET代理、网络或源站主动断开了连接记录失败发生在哪一跳;只对可安全重放的请求做有限次数重试
Nginx 后面的 502 Bad GatewayNginx 的 proxy_pass 配错了,或者 Axios 的超时和 Nginx 不一致检查 proxy_connect_timeoutproxy_read_timeout(默认都是 60 秒),并让它们和 Axios 的 timeout 对齐
ERR_TLS_CERT_ALTNAME_INVALID目标协议用错了 agent,或者证书是自签名的确认你用的是正确协议对应的 agent;本地测试时可临时设 rejectUnauthorized: false,但生产环境绝对不要这么做

快速排查清单

当你遇到问题又一时看不出原因时,就按这个顺序排:

  1. 先用 curl -x http://host:port https://your-target.com 直接测试代理。如果失败,先查代理连通性、认证和目标站,再去动 Axios。如果成功,Axios 这条链路仍然需要单独验证。
  2. 确认你实际运行的 Axios 和 Node 版本,并在应用同样的版本和配置前,先对照历史报告。
  3. 搞清楚到底是谁在解析代理:原生 Axios 配置、Axios 的环境变量解析、Node 内置环境代理模式,还是显式 agent。不要让多个系统同时负责同一条请求。
  4. 检查 NO_PROXY,看看是不是误匹配了主机名。
  5. 如果你用了显式 agent,确认已经设置 proxy: false,避免 Axios 再插手处理一遍。

什么时候干脆别自己折腾代理底层

如果你的真实目标是路由任意流量——比如做公司网络测试、地理位置测试,或者控制出口流量——上面这些内容当然有用。但很多开发者之所以搜“怎么给 Axios 配代理”,其实真正想要的是网站数据,而代理只是实现手段。

如果你的情况是这样,就值得认真想一想:你到底需不需要代理,还是需要一个帮你把基础设施都处理好的 抓取 API?Thunderbit 的 Open API 只需要传入一个 URL 和 schema,就能返回结构化 JSON——不用手动解析 HTML,不用装 agent 库,也不用自己维护代理池。`/extract` 接口 会在服务端处理 JS 渲染页面、反爬机制和 CAPTCHA;如果你只需要把页面整理成干净的 Markdown,也可以用更轻量的 `/distill` 接口。它还有一个 MCP server,提供像 thunderbit_extractthunderbit_suggest_fields 这样的工具,让 Claude 或 Cursor 这类编码助手在任务过程中直接拉取结构化数据,而完全不需要碰代理配置;另外还有一个 CLI,适合终端和 CI 工作流。

关注点自己搭 Axios + 代理Thunderbit API / MCP / CLI
代理来源与轮换你自己管理服务端处理
浏览器和访问挑战你负责浏览器/网络层由服务在其文档能力范围内代管
JS 渲染页面需要无头浏览器renderMode: full
输出格式原始 HTML → 你自己解析通过 schema 输出结构化 JSON
网站变化后的维护你自己维护托管式提取层能减少部分应用侧维护成本

说实话,如果你的目标是做测试或企业网络转发,那这些内容并不能替代 Axios 和代理配置。但如果你最终要的是结构化网页数据,API 优先的方式很可能能帮你少写很多代理、浏览器和解析代码。根据 2026 年 8 月 7 日检索时的信息,Thunderbit 的 API 限流文档 显示其免费套餐为每分钟 10 次请求、并发 2 次请求。这些都是会随时间变化的 API 限制,生产环境里务必在依赖前重新查看页面。

总结

这篇文章的核心结论,和很多老教程的说法正相反:当前 Axios 文档明确支持,而且在我们记录的本地测试中,HTTPS 目标确实走了 CONNECT 隧道。历史上的故障当然仍然重要,但必须放在对应版本和配置背景下来看。原生配置适合作为低复杂度的起点;当你需要按请求控制、SOCKS 支持或代理轮换时,配合 proxy: false 的显式 HttpsProxyAgent(或 SocksProxyAgent)能让责任边界更清楚。如果你是在生产环境里做代理池轮换,请求和响应拦截器能把逻辑集中到一个可测试的位置——但记得给重试加上循环保护,而且只重放那些真正安全重放的请求。

把上面的 诊断表 收藏起来,下次半夜 2 点代理报出一串看不懂的错误时,你会感谢现在的自己。要是你发现自己花在排查代理底层的时间,比真正使用数据的时间还多,也许该考虑一下:一个 API 优先的数据提取工具 可能比继续搭基础设施更快解决问题。

常见问题

Axios 原生支持 HTTPS 代理吗? 当前 Axios 对常规 HTTP 代理是支持的:官方文档说明了 HTTPS 目标会通过 CONNECT 隧道转发,而 Axios 1.19.0 在我们记录的本地测试中也确实走通了这条路径。历史版本和某些特定代理配置确实会出问题,所以不要想当然地认为一定成功或一定失败,关键还是要看具体版本和代理设置。

如何在 Axios 里轮换代理? 在请求发出前,用请求拦截器从代理池中挑选一个不同的 httpsAgent;再配合响应拦截器,对失败请求用另一个代理做重试。重试逻辑一定要有边界——只重试一次,而且只对像 GET 这种幂等方法重试——这样就不会误把不该重复的请求再发一遍。

为什么我的 Axios 代理显示的是真实 IP? 检查是否有 NO_PROXYproxy:false、显式直连 agent,或者部署环境中的路由规则把代理绕过去了。记录 Axios/Node 版本,并用 cURL 独立测试代理。如果你需要毫无歧义的按请求路由,就用 HttpsProxyAgent 配合 proxy:false,再在受控端点上验证看到的 IP。

Axios 能用 SOCKS5 代理吗? 可以,通过 socks-proxy-agent 包来实现。创建一个 SocksProxyAgent 实例,把 SOCKS URL 传进去,然后作为 Axios 配置里的 httpsAgent 使用——但别同时再传 HttpsProxyAgent,因为这两种协议对应的是不同的 agent 类型。

Axios 里的 proxy 选项和 httpsAgent 有什么区别? proxy 是 Axios 内置的单代理静态配置,适合当前版本里的简单场景。httpsAgent 则允许你接入自定义的 Node.js agent,比如 HttpsProxyAgentSocksProxyAgent,让你能按请求直接控制路由、认证和轮换;这本来就不是内置选项的设计目标。

了解更多

Ke
Ke
Thunderbit 首席技术官 | 高级数据科学家与机器学习专家 Ke Shen 拥有近十年的机器学习和数据科学经验,毕业于哥伦比亚大学,曾任 Walmart Labs 高级数据科学家。他在 Python、R、Java 和统计学方面拥有深厚且备受同行认可的专业能力,并分享如何将复杂的 AI 算法从理论落地到生产级架构的实战经验。
Topics
Axios 代理Node.js 代理HTTPS 代理
目录
Thunderbit · AI 网页数据代理

1 次点击 中从任何页面提取数据

受到超过 250,000+ 用户的信赖
提供免费计划
使用 AI 提取数据
轻松将数据传输到 Google Sheets、Airtable 或 Notion
Chrome Store Rating
PRODUCT HUNT#1 Product of the Week