Skip to content

Vite Proxy、Node 代理环境变量与 Clash 分流不生效问题排查

本文记录本仓库联调 http://192.200.125.79:9097 时的实机结论。开发机:macOS + Clash Verge(系统代理开、TUN 关)+ Node 24.19.0 + pnpm dev(Vite :3333)。

一、现象

  • 浏览器打开 http://localhost:3333/obd/api/sys/captchaImage 有时正常。
  • http://localhost:3333/obd/api/sys/publicKey 一直 pending / 超时。
  • Clash 已配规则脚本:
js
"IP-CIDR,192.200.125.0/24,DIRECT,no-resolve"
  • Clash「代理绕过」已加 192.200.125.0/24192.200.125.79
  • ~/.zshrc 已配 NO_PROXY=...,192.200.125.79,同一终端 echo $NO_PROXY 也能看到。
  • 给 Vite /obd 配直连 http.AgentproxyEnv: {})后立刻正常。

根因不是「Clash 规则写错了」,而是:浏览器、Clash 规则、Node/Vite 出站不是同一条代理链路。


二、实际请求链路

登录页 axios 的 VITE_OBD_BASE_URL=/obd,浏览器只打本机:

浏览器  →  localhost:3333/obd/...  →  Vite Dev Server
                                      (Node http.request)
                                      →  192.200.125.79:9097/obd/...
角色看什么
浏览器访问 localhost:3333macOS 系统代理 + Clash「代理绕过」(ExceptionsList
浏览器直接打开 http://192.200.125.79:9097/...同上
Vite 转发 /obd只认进程环境变量 HTTP_PROXY / NO_PROXY,以及 http.Agent不读 Clash 规则,也不读 macOS 绕过列表

Clash 规则(IP-CIDR ... DIRECT)只处理 已经进到 Clash 里的包。Vite 若带着 HTTP_PROXY=http://127.0.0.1:随机端口 出站,会先打 Clash 的 HTTP 端口;端口失效(Clash 重启后端口会变)就会一直卡住,规则轮空。

有 HTTP_PROXY 且 Node 启用了环境代理:
  Vite → 127.0.0.1:Clash端口 →(此时才匹配规则)→ DIRECT / 代理节点

无 HTTP_PROXY,或 Agent.proxyEnv = {}:
  Vite → 192.200.125.79:9097    (根本不进 Clash,规则不执行)

三、为什么 Clash 配了还是不生效

1. 规则 ≠ 绕过 ≠ NO_PROXY

配置作用对象对本仓库 Vite /obd
IP-CIDR,192.200.125.0/24,DIRECT已进入 Clash 的流量不保证 Node 不进 Clash
Clash「代理绕过」/ system_proxy_bypassmacOS ExceptionsList(浏览器)Node 不看
export NO_PROXY=...192.200.125.79仅当 Node 启用了环境代理时见下一节

192.200.125.0/24 不是 192.168/10/172.16 那种私网,Clash 默认「绕过局域网」管不到。

2. Node 24 默认不看 NO_PROXY

本机实测(Node 24.19.0):

环境http.request192.200.125.79
仅有 HTTP_PROXY,无 NODE_USE_ENV_PROXY直连(不走代理,NO_PROXY 无意义)
NODE_USE_ENV_PROXY=1 + HTTP_PROXY + NO_PROXY 含该 IP直连
NODE_USE_ENV_PROXY=1 + HTTP_PROXY + NO_PROXY 为空打到 127.0.0.1:代理端口

因此:只在 zshrc 里写 NO_PROXY、不设 NODE_USE_ENV_PROXY=1,对 Vite 无效。
Clash 也不会把「代理绕过」写进进程的 NO_PROXY(常见仍是 127.0.0.1,::1,localhost)。

Cursor / VS Code 从程序坞启动,不读 ~/.zshrc。必须在 即将执行 pnpm dev 的那个终端 里检查:

bash
echo $HTTP_PROXY
echo $NO_PROXY
echo $NODE_USE_ENV_PROXY

改完 zshrc 或开关 Clash 系统代理后,要 完全退出编辑器再打开,旧窗口会一直带着启动时的变量。Clash 系统代理端口还会变(如 532595838910808),旧 pnpm dev 连已死端口会表现为接口一直转圈。

3. Vite agent 的坑

http-proxy-3 默认 agent = options.agent \|\| false

写法结果
不写 agent / agent: false每次新 Agent;是否走 HTTP_PROXY 取决于 Node 是否启用环境代理
proxyEnv: falsenullNode 24 会把请求卡死,不要用
new http.Agent({ proxyEnv: {} })该 Agent 不读 HTTP_PROXY,本仓库已验证可通

proxyOptions: { agent: false } 不是 Vite 的合法字段,不会生效


四、怎么确认卡在哪

  1. 终端(与 pnpm dev 同一窗口):
bash
env | grep -i proxy
echo $NODE_USE_ENV_PROXY
  1. 浏览器地址栏(不要用终端 curl 代替浏览器):
  • http://127.0.0.1:3333/obd/api/sys/captchaImage?dt=1
  • http://127.0.0.1:3333/obd/api/sys/publicKey
  • http://192.200.125.79:9097/obd/api/sys/publicKey

前两条走 Vite;第三条走浏览器系统代理。Vite 那条 pending、直连 9097 却很快,就是 Node 出站(HTTP_PROXY / 死端口)。两条都 pending,才是 9097 本身或网线(en13 到该网段)。

  1. curl 会认 macOS 系统代理,和 Node 不一致,排查时加 --noproxy '*'

五、本仓库现状(项目内已处理)

vite.config.ts/obd 强制直连,不依赖本机 NO_PROXY 是否生效:

  • 启动时把 192.200.125.79 写入进程 NO_PROXY / no_proxy
  • http.Agent({ keepAlive: true, timeout: 10000, proxyEnv: {} })
  • timeout / proxyTimeout: 10000,避免无限 pending

vite.config.ts 后必须重启 pnpm dev

其它访问 192.200.*Node 项目,若未做同样处理,在「系统代理开着」的机器上仍可能中招。浏览器直开该 IP 只看 Clash 绕过 / TUN。


六、不改项目、只改本机

按可靠性:

1. 关系统代理 + 开 TUN(推荐)

  • Clash:关「系统代理」,开 TUN
  • 保留规则:IP-CIDR,192.200.125.0/24,DIRECT,no-resolve
  • TUN route-exclude-address192.200.125.0/24(本机 Verge 已有)
  • 完全退出 Cursor / VS Code 再开,再 pnpm dev

系统代理一关,就不会给 Node 注入 HTTP_PROXY

2. 启动时清掉代理(不改仓库)

bash
env -u HTTP_PROXY -u HTTPS_PROXY -u ALL_PROXY -u http_proxy -u https_proxy -u all_proxy pnpm dev

3. 让 Node 真的认 NO_PROXY(只写 NO_PROXY 不够)

~/.zshrc

bash
export NODE_USE_ENV_PROXY=1
export NO_PROXY="localhost,127.0.0.1,::1,192.200.125.79"
export no_proxy="$NO_PROXY"
bash
source ~/.zshrc
# 给 GUI 进程也带上(可选)
launchctl setenv NO_PROXY "localhost,127.0.0.1,::1,192.200.125.79"

完全退出编辑器,新终端里确认 $NO_PROXY 含该 IP,且没被 Clash 覆盖成只有 localhost


七、结论

误区实际
Clash DIRECT 了,Vite 就会直连Vite 可能根本没进 Clash,或卡在已死的本地代理端口
配了「代理绕过」Node 也会绕那是给浏览器的;Node 只看环境变量 + Agent
zshrc 写了 NO_PROXY 就够Node 24 还要 NODE_USE_ENV_PROXY=1,且编辑器要重开
agent: false / proxyOptions.agent前者不一定关掉环境代理;后者 Vite 不认
proxyEnv: false会卡死请求

Clash 管进入 Clash 的流量;Vite proxy 是 Node 自己的出站 HTTP。两套体系。 本仓库用直连 Agent 兜底;本机若关系统代理并开 TUN,其它 Node 项目也可少踩坑。

最近更新