周一至周五 09:00 - 18:00(UTC+08:00) 
©2026 NST LABS TECH LTD. 保留所有权利。 Node.js 代理:如何在 Node.js 中使用代理服务器Ivy LinCommunity & Content Lead
什么是Node.js代理以及如何在Node.js中使用代理服务器
摘要
- Node.js 没有内置的代理支持。 核心的
http/https 模块直接将请求发送到目标主机,除非你附加一个支持代理的 http.Agent 或自己设置请求选项。
HTTP_PROXY/HTTPS_PROXY 环境变量仅在你调用的库读取它们时才有效。 核心的 http.request 完全忽略它们;大多数命令行工具和一些 HTTP 客户端通过辅助包读取这些变量,而不是自动读取。
https-proxy-agent 和 http-proxy-agent 是通过代理路由核心 http/https 请求的标准方式, 使用 HTTP CONNECT 隧道处理 HTTPS 目标,直接转发处理 HTTP 目标。
- Axios 直接接受一个
proxy 对象(host,port,protocol,可选的 auth),因此大多数基于 Axios 的项目不需要单独的代理包来处理 HTTP 代理。
- 原生的
fetch 和 undici 通过 ProxyAgent 和 setGlobalDispatcher 使用代理, 这也影响 Node 的全局 fetch(),因为 Node 18 及以后版本是基于 undici 构建的。
- 代理凭证应该放在代理 URL 或专用的身份验证字段中,绝不应硬编码在会提交到源代码管理的应用程序代码中。
- SOCKS5 代理需要一个不同的代理(),因为基于 的 HTTP 代理代理不理解 SOCKS 握手。
socks-proxy-agent
CONNECT
引言:Node.js 请求中的“代理”含义
在 Node.js 中,代理是一个中介服务器,脚本故意通过它路由出站的 HTTP 或 HTTPS 请求,而不是直接连接到目标主机。Node 的核心 http 和 https 模块并没有内置代理支持作为默认行为——在 Node.js 中,每个经过代理的请求都是因为代码(或库)明确将请求指向代理主机,或者通过自定义的 http.Agent,或者通过客户端特定的配置。
这个区别很重要,因为它解释了为什么将为一个 HTTP 客户端编写的代理教程复制到使用不同客户端的项目中往往无效。curl 和许多系统工具会自动从环境中读取 HTTP_PROXY/HTTPS_PROXY;而 Node 的 http.request 则不会。下面的每个客户端库都需要其自己的显式设置。
快速了解
将代理凭证和轮换逻辑接入每个请求很容易出错——Nstproxy 为每个计划提供一个稳定的网关终端,因此你的 Node.js 代码只需指向一个代理 URL。
安装所需的包
Node.js 附带 http,https 和(从 Node 18 开始)一个由 undici 的 ProxyAgent 支持的全局 fetch。它们都不包含代理路由代码,因此添加与您已经使用的客户端匹配的代理包:
npm install https-proxy-agent http-proxy-agent
npm install axios
npm install node-fetch@2
npm install undici
npm install socks-proxy-agent
为 Node 的核心 http/https 模块配置代理
http-proxy-agent 通过代理中继普通的 HTTP 请求;https-proxy-agent 通过代理打开一个 HTTP CONNECT 隧道,然后与目标协商 TLS,这就是 HTTPS 目标所要求的。将结果代理作为 agent 选项传递给 Node 的 http.Agent 基础的请求函数,是对正常的 https.request 调用的唯一更改:
const { HttpsProxyAgent } = require('https-proxy-agent');
const https = require('https');
const agent = new HttpsProxyAgent('http://user:pass@proxy.example.com:8000');
https.get('https://api.example.com/status', { agent }, (res) => {
let body = '';
res.on('data', (chunk) => (body += chunk));
res.on('end', () => console.log(res.statusCode, body));
});
这个示例是在一个本地测试代理(一个处理 CONNECT 的 Node http.createServer)和一个本地 HTTPS 目标上运行的:请求通过隧道返回了 200 和预期的 JSON 正文,确认了 CONNECT 然后 TLS 流在这个确切的代理版本和调用形状下是有效的。
对于普通的 HTTP(而非 HTTPS)目的地,请改用 HttpProxyAgent —— 它在没有 CONNECT 握手的情况下转发请求:
const { HttpProxyAgent } = require('http-proxy-agent');
const http = require('http');
const agent = new HttpProxyAgent('http://proxy.example.com:8000');
http.get('http://api.example.com/status', { agent }, (res) => {
let body = '';
res.on('data', (chunk) => (body += chunk));
res.on('end', () => console.log(res.statusCode, body));
});
对同一目标执行未修改的 http.get() 调用,没有 agent 选项,并且仅将 HTTP_PROXY 设置为环境变量,确认了核心的 http 会忽略该环境变量 —— 请求直接发送到了目标而不是通过代理。对任何告诉你“只需设置 HTTP_PROXY”的核心 http/https 教程要谨慎对待;它仅对特定读取该变量的工具有效。
在 Axios 中配置代理
const axios = require('axios');
axios.get('https://api.example.com/status', {
proxy: {
host: 'proxy.example.com',
port: 8000,
protocol: 'http',
auth: { username: 'user', password: 'pass' },
},
}).then((res) => console.log(res.status, res.data));
该请求是针对上面使用的相同本地代理和目标运行的,返回了 200 和预期的正文,确认 Axios 的 proxy 选项可以自动为 HTTPS 目标执行 CONNECT 隧道。如果你需要 Axios 的 proxy 对象没有暴露的 TLS 选项 —— 自定义 CA 或禁用对内部测试主机的证书验证 —— 可以将 httpsAgent: new https.Agent({...}) 与 proxy 一起传递,或用分配给 httpsAgent 的 https-proxy-agent 实例完全替换 proxy。
Axios 还默认尊重 HTTP_PROXY/HTTPS_PROXY/NO_PROXY 环境变量,除非在请求配置中设置了 proxy: false —— 这是少数几个在不需要你编写代理代码的情况下可以正常使用环境变量约定的客户端之一。
为 node-fetch 和原生 fetch/undici 配置代理
node-fetch(测试版本 2.7.0)使用与核心 http/https 相同的 agent 选项,因此同样适用 http-proxy-agent/https-proxy-agent 实例:
const fetch = require('node-fetch');
const { HttpProxyAgent } = require('http-proxy-agent');
const agent = new HttpProxyAgent('http://proxy.example.com:8000');
fetch('http://api.example.com/status', { agent })
.then((res) => res.json())
.then((json) => console.log(json));
针对本地测试代理和 HTTP 目标运行时,这返回了预期的 JSON 正文,确认相同的代理类在核心 http 和 node-fetch 中正常工作。
Node 内置的全局 fetch()(从 Node 18 开始可用)和独立的 undici 包并没有以相同方式接受 agent 选项 —— 它们使用 undici 自己的 ProxyAgent,通过 setGlobalDispatcher 全局注册:
const { ProxyAgent, setGlobalDispatcher } = require('undici');
setGlobalDispatcher(new ProxyAgent('http://proxy.example.com:8000'));
const res = await fetch('https://api.example.com/status');
console.log(res.status, await res.json());
这是利用 undici 版本 6.28.0 运行的:在调用 setGlobalDispatcher 之后,undici.request() 和 Node 的全局 fetch() 都通过本地测试代理进行了路由,并返回了预期的 200 响应 —— 确认 setGlobalDispatcher 影响全局 fetch,而不仅仅是 undici 自己的请求函数,因为 Node 的 fetch 实现是基于 undici 构建的。将 ProxyAgent 的范围限制在单个请求,而不是全局设定当只有处理中的某些请求需要通过代理时 —— 将 { dispatcher: new ProxyAgent(...) } 作为每次调用选项传递给 undici.request()。
高级模式:身份验证、SOCKS5 和轮换
代理身份验证。 对于以上四个客户端,将凭据嵌入代理 URL (http://user:pass@proxy.example.com:8000) 或客户端特定的身份验证字段(Axios 的 proxy.auth)。切勿将它们格式化为目标请求的 Authorization 头 —— 那个头部是发送到目标服务器的,而不是代理,且大多数代理服务器期望在 Proxy-Authorization 头中提供凭据,代理包构造的代理会从 URL 的用户信息生成这个头部。
SOCKS5代理。 https-proxy-agent和http-proxy-agent仅实现HTTP CONNECT代理协议。SOCKS5端点需要 socks-proxy-agent,它暴露相同的agent选项模式:
const { SocksProxyAgent } = require('socks-proxy-agent');
const https = require('https');
const agent = new SocksProxyAgent('socks5://user:pass@proxy.example.com:1080');
https.get('https://api.example.com/status', { agent }, (res) => { /* ... */ });
会话控制(粘性与旋转)。 是否给定请求重用与之前相同的出口IP,或获取一个新的,由代理提供商的网关控制,而不是由Node.js控制——上述客户端代码无论如何保持不变。支持这两种模式的提供商通常根据附加到代理用户名的会话参数(“粘性会话”ID)或为粘性与旋转池的单独网关端口来切换行为;在假定一个提供商的约定适用于另一个提供商之前,请查看特定提供商的网关文档以获取确切的参数名称。
使用Nstproxy相同模式
Nstproxy提供HTTP/SOCKS5网关端点,涵盖住宅轻量级代理和六个其他代理产品系列——住宅优质、数据中心、静态ISP、IPv6、无限住宅和移动——因此,以上的HttpsProxyAgent/Axios proxy/ProxyAgent代码通过将代理指向您分配的Nstproxy网关主机、端口和凭证来工作,而不是占位符。Nstproxy专为需要地理定向、会话控制或比单个自托管代理更高请求量的团队而构建,适合抓取、价格监控、广告验证和QA测试工作负载,其中单个出口IP会被限速或阻止。
- 国家和城市级定向——通过在代理用户名中包含位置参数选择出口位置,而无需更改任何客户端请求代码。
- 粘性或旋转会话——根据使用的会话参数,保持一个IP用于多步骤工作流(登录,然后分页)或每个请求旋转。
- HTTP和SOCKS5网关——上面显示的相同客户端代码(针对核心
http/node-fetch的基于代理的实现,Axios的本地proxy对象,undici/fetch的ProxyAgent)无需进一步更改即可对抗Nstproxy的网关。
诚实的限制
- 核心
http/https和node-fetch从不自行读取HTTP_PROXY/HTTPS_PROXY——上述每个示例都在代码中显式设置代理,任何仅依赖环境变量的部署对于这些客户端将会在静默中绕过代理。
https-proxy-agent的CONNECT隧道相比于直接请求,每个新的HTTPS连接增加一个额外的网络往返;连接重用(keepAlive: true在代理上)将这一成本分摊到对同一主机的多个请求中。
- 上述代理包没有自动重试失败的代理连接或切换到不同的出口IP——如果您需要这项逻辑,则必须在自己的请求包装中编写或由代理服务的网关提供。
- WebSocket连接需要它们自己的代理感知升级处理;此处显示的代理仅覆盖简单的HTTP/HTTPS请求/响应周期,而不是
Upgrade握手。
故障排除
通过代理连接时出现ECONNREFUSED或ECONNRESET。 确认代理主机和端口是否可以直接访问(例如使用nc -vz host port),然后再假定Node.js代码有错——防火墙或过期的代理会话会产生与代码错误相同的错误。
无法验证第一个证书或自签名证书错误。 这意味着客户端正在通过隧道验证目标主机的TLS,但失败了,通常是因为企业或测试代理使用自己的证书拦截TLS。将该证书添加到Node的受信任存储中(NODE_EXTRA_CA_CERTS),而不是在生产代码中禁用证书验证。
请求挂起而不是失败。 在代理或客户端上设置明确的超时(http.request 的 timeout 选项,Axios 的 timeout)— 否则,在平台的默认套接字超时之前,默默丢弃数据包的代理将挂起,无法返回连接拒绝错误。
只有部分请求使用代理。 对于 undici/global fetch,setGlobalDispatcher 适用于进程中的每个后续调用;如果某些调用意外绕过代理,请检查该代码路径是否使用其他需要自己单独代理配置的 HTTP 客户端(Axios,node-fetch)。
结论
在 Node.js 中的每个代理设置最终归结为同一个决策:哪个 HTTP 客户端正在发起请求,以及应该使用该客户端支持的哪种代理机制——明确的 http.Agent、proxy 配置对象或全局调度器——你将其与目标主机、端口和凭据进行配置。核心的 http/https 和 node-fetch 需要来自 https-proxy-agent/http-proxy-agent/socks-proxy-agent 的代理;Axios 直接接受 proxy 对象;本地 fetch 和 undici 通过 ProxyAgent 和 setGlobalDispatcher 路由。除非特定客户端文档说明它读取,否则这些都不能单靠 HTTP_PROXY 环境变量工作。
常见问题
不支持——核心 http 和 https 模块直接连接到目标主机,除非请求明确使用支持代理的 http.Agent(来自诸如 https-proxy-agent 的包)或客户端库有自己的代理配置,例如 Axios 的 proxy 选项。
问:为什么在我的 Node.js 脚本中设置 HTTP_PROXY 不有效?
设置 HTTP_PROXY 仅对明确读取它的客户端有效——Axios 默认会读取它,但核心 http/https 和 node-fetch 不会,因此它们无论环境变量如何都需要在代码中明确指定代理。
问:http-proxy-agent 和 https-proxy-agent 的区别是什么?
http-proxy-agent 通过代理转发普通 HTTP 请求,而不建立隧道,而 https-proxy-agent 首先通过代理打开一个 HTTP CONNECT 隧道,然后与 HTTPS 目标协商 TLS——使用与目标方案匹配的代理,而不是匹配代理的方案。
问:我可以在 Node 的原生 fetch() 中使用代理吗?
可以——在调用 fetch() 之前使用 setGlobalDispatcher() 注册一个 undici ProxyAgent,因为 Node 的内置 fetch 在 undici 上运行并读取相同的全局调度器。
问:我需要为 SOCKS5 代理进行不同的设置吗?
是的——https-proxy-agent 和 http-proxy-agent 仅实现 HTTP CONNECT 代理协议,因此 SOCKS5 端点需要使用 socks-proxy-agent,采用相同的 agent 选项模式。
问:在我的 Node.js 代码中硬编码代理凭据安全吗?
不安全——将代理用户名和密码保存在环境变量或秘密管理器中,并在运行时构建代理 URL,就像处理数据库密码一样,而不要将其提交到源代码管理中。
通过 HTTP CONNECT 隧道的 HTTPS 请求相比直接连接多增加了一次往返旅行以建立隧道,但在代理上启用 keepAlive 可让后续请求重用该隧道,而不是再次支付费用。
立即访问住宅、数据中心、IPv6 与 ISP 高质量代理池。