curl 访问 127.0.0.1 却返回 502:本机代理环境变量埋的坑
先说现象。本机起了个服务监听 127.0.0.1:9222,浏览器能正常打开,但命令行 curl 一直返回 502 Bad Gateway,日志里服务端一条请求记录都没有。换端口、重启服务、换 curl 版本都没用,因为它压根没连到你本机的服务。
为什么代理会劫持回环地址
很多人以为「本机地址不会走代理」,实际不是。curl 的行为是:只要环境变量里存在 http_proxy / https_proxy / all_proxy,它对任何 http(s) URL 都会先连代理,包括 http://127.0.0.1 和 http://localhost。是否直连只由 NO_PROXY 决定,而 NO_PROXY 默认是空的。
于是链路变成:curl → 代理(127.0.0.1:14542)→ 代理尝试访问 127.0.0.1:9222。注意后一个 127.0.0.1 是代理进程自己所在主机的回环,如果代理跑在容器、WSL、远程跳板上,它看到的 127.0.0.1 跟你不是同一台机器,连不上就回 502。就算代理在本机,很多代理也会拒绝转发回环地址,同样回 502。
所以 502 这个状态码特别有迷惑性:它看起来像「后端服务挂了」,实际含义是「代理连不上上游」。真正的判据是服务端没有任何访问日志——服务没崩,是流量没到。
怎么快速确认
第一,直接打印环境变量:
env | grep -i proxy
如果 HTTP_PROXY / http_proxy 有值而 NO_PROXY 是空的,基本就锁定了。注意大小写两套变量都要看,curl 两套都认,且小写优先。
第二,绕开代理再请求一次:
curl --noproxy '*' http://127.0.0.1:9222/health
如果这一下通了,结论就确定了:不是服务的问题,是代理的问题。
第三,看 curl 的详细过程:
curl -v http://127.0.0.1:9222/health
走代理时输出里会先出现 Trying 127.0.0.1:14542... 然后 CONNECT 或直接 GET 到代理端口;直连时才是 Trying 127.0.0.1:9222。这一步能把「以为直连、其实走了代理」直接照出来。
三种修法,按推荐顺序
1)补 NO_PROXY(最省事,一次配好长期有效)
export NO_PROXY=localhost,127.0.0.1,::1,.local
export no_proxy=$NO_PROXY
要点:只写域名不够,必须带 127.0.0.1 和 ::1;很多工具只认小写那一份,两份都设最稳;用 CIDR 或 127.0.0.0/8 在部分工具里不生效,老老实实列地址更可靠。
2)只给这一条命令关代理
curl --noproxy '*' ...
适合临时排查、写进脚本给不特定环境跑。缺点是容易忘,一换成 wget、httpie、Node 的 http 模块就又不生效了——它们各自读环境变量的逻辑并不完全一样。
3)改脚本里的请求方式
如果代码里用的是 axios、requests 这类库,它们默认也会读环境变量。要么在初始化时显式传 proxy: false(axios)或不设 proxies(requests 用 trust_env=False),要么统一走 NO_PROXY。这一步比在命令行加参数更彻底,因为定时任务、CI 里通常没人会记得加。
两个容易连带踩到的点
一是「502 是真故障」的误判。看到 502 先别急着查后端进程,先确认流量有没有到后端:后端日志有没有记录、端口用 ss -lntp 看是不是真在 listen。两者只要有一个不成立,问题就在链路上,不在服务里。
二是 IPv6。localhost 在不少系统上优先解析成 ::1,服务只监听了 127.0.0.1 就会出现「curl localhost 不通、curl 127.0.0.1 通」的怪现象。这跟代理无关,但症状相似,排查时一起看一眼比较省时间:ss -lntp 里能看到监听的是 0.0.0.0、127.0.0.1 还是 [::]。
小结
127.0.0.1 不通、返回 502、后端零日志,这三件事同时出现,九成是代理环境变量把回环流量带走了。判断只需要一条 curl --noproxy '*',修法优先补 NO_PROXY,脚本内部再单独关一次,别指望命令行参数能覆盖所有调用方。
- 点赞
- 收藏
- 关注作者
评论(0)