Node.js脚本里设置了 HTTP_PROXY,浏览器也能走代理,但 fetch() 仍然直连,这并不罕见。现代Node.js内置fetch与Undici生态紧密相关,其代理配置、全局Dispatcher和环境变量行为要按当前Node与Undici版本确认,不能直接套用旧版 http.Agent 示例。
一、先确认运行环境
- Node.js具体版本;
- 使用内置fetch、undici包、axios还是其他库;
- 代码运行在本机、容器、Serverless还是CI;
- 是否存在HTTP_PROXY、HTTPS_PROXY、NO_PROXY或平台注入;
- 是否需要HTTP代理、HTTPS代理或SOCKS5;
- 是否使用企业TLS检查和自定义CA。
不同库不必共享同一Agent接口。
二、Dispatcher是什么
Undici使用Dispatcher抽象连接调度。代理场景通常通过当前版本支持的ProxyAgent或环境代理Agent创建Dispatcher,再传给请求或设置受控的全局Dispatcher。
具体构造函数和环境代理能力可能随版本变化,必须参考当前官方文档。不要复制未标注Node版本的代码。
三、局部Dispatcher优于全局修改
如果只有一个外部API需要代理,把Dispatcher传给该客户端更可控;全局设置会影响同一进程内所有使用Undici的请求,可能让健康检查、内部服务和元数据请求改变路径。
封装一个外部API客户端,明确代理、超时和关闭生命周期,比在应用入口全局修改更容易测试和回滚。
四、环境变量为什么可能不生效
环境变量是约定,不是所有Node HTTP库自动遵守的规则。要确认:
- 当前Node/Undici是否提供环境代理Agent;
- 是否显式启用;
- 大小写变量和优先级;
- NO_PROXY语法与端口匹配;
- 应用启动后才修改变量是否已错过读取时机。
五、SOCKS5不能直接当HTTP代理
HTTP ProxyAgent通常按HTTP CONNECT等方式工作,SOCKS5是不同协议。若业务必须使用SOCKS5,应选择明确支持当前Node版本和fetch接口的可信实现,并验证DNS、IPv6和认证。
协议区别可参考SOCKS5与HTTP代理选型。
六、连接池与进程退出
Dispatcher通常维护连接池。频繁为每个请求创建Agent会增加握手和资源消耗;长期进程应复用并在关闭时正确释放。测试脚本若不关闭连接,进程可能继续等待。
切换代理或凭据时创建新Dispatcher,停止给旧池分配请求,待在途请求完成后关闭。
七、超时和AbortSignal
fetch需要明确超时和取消策略。可以使用当前Node支持的AbortSignal工具或自建AbortController,但要区分总截止时间与连接/响应阶段。取消后确认响应体和连接是否正确清理。
不要让任务无限等待代理连接。
八、TLS和企业CA
HTTPS经代理出现证书错误时,不应设置 NODE_TLS_REJECT_UNAUTHORIZED=0。该变量会削弱整个进程的TLS验证,风险远超单个请求。
应确认目标域名、SNI、证书链、企业CA和当前Agent配置。参考代理TLS与证书排查。
九、NO_PROXY要保护哪些地址
内部API、数据库网关、localhost、容器服务名、云元数据和必须直连的管理端点需要按实际架构评估。环境Agent是否支持域名后缀、端口、IPv6和CIDR,应通过当前实现测试。
十、分层验证
- 用内置fetch访问自有外部健康端点;
- 在代理日志确认节点和来源;
- 访问内部测试端点,确认NO_PROXY直连;
- 验证407、连接超时和TLS失败;
- 核对IPv4、IPv6和DNS路径;
- 关闭Dispatcher,确认进程退出和资源释放。
十一、常见故障
| 现象 | 方向 |
|---|---|
| HTTP_PROXY有值但fetch直连 | 当前fetch未自动读取或未启用环境Agent |
| axios能用,fetch不能用 | 两者Agent和代理配置接口不同 |
| 内部地址也走代理 | 全局Dispatcher或NO_PROXY问题 |
| 切换后仍旧出口 | Dispatcher连接池复用 |
| 脚本不退出 | 连接池或响应体未关闭 |
十二、日志安全
不要记录Dispatcher内部完整URL、代理userinfo、Cookie或Authorization。使用节点ID、配置版本、请求ID和错误阶段追踪。
Node.js代理稳定性的关键,是把版本、客户端、Dispatcher和生命周期写清楚,而不是只在进程环境里放一个变量后期待所有库自动响应。






