curl 设置代理为什么不生效?HTTP_PROXY 大写不生效,问题在大小写规则
在终端里写几行命令,想让 curl 走代理出口:
export HTTP_PROXY="http://127.0.0.1:7890"
curl -v https://example.com
结果翻日志,看到的是 Trying <example.com 的 IP>——curl 根本没有先连到代理端口,请求直接发向了目标站点。很多人第一反应是代理软件没开、端口写错、网络不通,反复折腾半天,最后发现问题出在变量名的大小写上:curl 对 http:// 目标只识别小写 http_proxy,大写 HTTP_PROXY 被刻意忽略。
这篇文章讲清三件事:curl 到底读哪些代理环境变量、为什么唯独 http_proxy 只认小写、除了大小写之外代理不生效还可能卡在哪,最后给出一套按顺序排查的清单。
01 先搞清楚:curl 到底读哪些代理环境变量
一句人话:curl 发起请求前,先按目标 URL 的协议类型去查对应环境变量;命令行参数优先级最高,其次是用户配置文件,最后才是环境变量。
| 变量 | 作用对象 | 大小写规则 | 说明 |
|---|---|---|---|
http_proxy |
http:// 目标地址 |
只认小写 | 为安全设计,下文展开说明 |
https_proxy / HTTPS_PROXY |
https:// 目标地址 |
大小写均可 | 当前绝大多数流量走 HTTPS,这个变量最常被漏掉 |
all_proxy / ALL_PROXY |
未被上面命中的其他协议 | 大小写均可 | 兜底代理 |
no_proxy / NO_PROXY |
需要直连、跳过代理的主机清单 | 大小写均可 | 逗号分隔;* 表示全部跳过;前导点匹配域名及子域名;较新版本支持网段写法 |
一个容易混淆的区分:变量名选的是目标 URL 的协议,变量值里的 scheme 描述的是客户端怎么连代理。所以 https_proxy="http://proxy:port" 不是写错了:对 HTTPS 目标,curl 通常先向代理发送 CONNECT 建立隧道,再在隧道里完成 TLS,代理本身用 HTTP 接入是常规组合。
优先级从高到低依次为:命令行 -x/--proxy 参数、用户配置文件 .curlrc、环境变量;单次命令里的 --noproxy 可以覆盖环境中的直连名单。

终端请求先按协议分流到对应代理变量,再由网关转发到目标站点。
02 为什么唯独 http_proxy 只认小写:一条延续多年的安全设计
curl 按目标协议名查找环境变量时,对 http 这一项只查找小写 http_proxy,大写 HTTP_PROXY 直接跳过;其余协议变量两种写法都接受。这不是拼写约定,而是一条刻意保留的安全防护。
背景要从 CGI 规范说起。CGI(RFC 3875)会把客户端发来的 HTTP 请求头转成 HTTP_ 前缀的环境变量:一个名为 Proxy: 的请求头,会被服务器写成 HTTP_PROXY。如果程序运行在 CGI 环境里又轻信这个变量,攻击者就可以通过这个可控的请求头,把服务器自己发往外部的 HTTP 请求劫持到恶意代理上。2016 年公开披露的 httpoxy 漏洞正是这一类问题,波及 PHP、Python、Go、Apache HTTP Server、Tomcat 等多类运行时,对应 CVE-2016-5385、CVE-2016-5386、CVE-2016-5387、CVE-2016-5388 等编号。curl 在这一问题公开之前就已经不再读取大写形式,以避免同样的风险被利用。
这里有两个边界需要讲清楚,避免从一个误区走到另一个误区:
- 这不是"所有代理变量都必须小写"。
HTTPS_PROXY、ALL_PROXY、NO_PROXY大小写都能识别;最稳妥的习惯是整套变量统一写成小写,避免不同工具、不同平台之间出现其他兼容差异。 - Windows 的环境变量名本身不区分大小写,在 Windows 终端里大写写法可以被识别;但小写写法在 Linux、macOS、容器和 Windows 上都成立,是跨平台最通用的选择。

同一套规则里,只有 http 这一项为了安全只放行小写形式。
03 除了大小写,代理不生效还常见这几类原因
大小写只解释了其中一种情况。实际排查时,按下面四组方向依次核对,能覆盖大多数"配了代理却还是直连"的场景。
3.1 变量没有真正进入当前进程
- 只在命令行临时赋值、没有
export:在 bash、zsh 下写成HTTP_PROXY=... curl ...只对该条命令生效,而且大写HTTP_PROXY本身不被 curl 读取。 - 在一个终端窗口里 export,换了新终端、新开脚本或 IDE 子进程后,环境变量已经丢失。
sudo默认剥离环境变量;需要保留时使用sudo -E。- Windows PowerShell 里
curl默认是Invoke-WebRequest的别名,参数规则和原生 curl 不同;要使用原生 curl,应敲curl.exe。
3.2 协议变量配错了对象
- 只设了
http_proxy,访问的却是https://地址:HTTPS 目标去查https_proxy/HTTPS_PROXY,没配自然走直连。 - 代理地址缺少
http://前缀、端口写错,或者把 SOCKS5 地址接到了 HTTP 端口上,握手直接失败。 - 代理要求鉴权但没有传入账号密码:日志中出现
407 Proxy Authentication Required表示代理拒绝了请求,问题在代理侧;可用--proxy-user "user:pass"传入凭据,或在代理 URL 中内嵌账号密码;密码包含@、:等字符时需要做 URL 编码。
3.3 no_proxy 把目标主机绕开了
- 清单里写了多余的域名后缀,或者把目标站点错误地加进了直连名单。
- 分隔符写成分号、逗号后留空格、误带
http://前缀,都会让名单匹配失效。 - 匹配是文本层面的:
example.com匹配自身及子域名,www.example.com只匹配该主机;不带前导点时,子域名覆盖情况要以所用 curl 版本手册为准;写主机名不会先解析成 IP 再匹配。
3.4 配置残留与其他来源的代理设置
~/.curlrc(Windows 下为_curlrc或.curlrc)里留着旧代理配置,它的优先级高于环境变量;用-q作为第一条参数可以跳过配置文件,用于临时隔离排查。- 别名、包装脚本或公司网络策略覆盖了命令行设置。
- 不少工具有自己独立的代理配置:Git 读取
http.proxy,npm 读取proxy/https-proxy,Docker 守护进程不继承 shell 变量;这些工具配置里的代理,不受环境no_proxy过滤。
把常见日志和现象对照起来,可以先做一轮分诊:
| 日志或现象 | 大概率卡点 | 先查什么 |
|---|---|---|
Trying <目标站点 IP> 后直连 |
变量未被识别 | 变量名大小写、是否 export、目标协议对应的变量是否配齐 |
| 连上代理后返回 407 | 代理鉴权失败 | 账号密码、白名单是否生效、特殊字符是否编码 |
| Could not resolve proxy / 连接被拒绝 | 代理入口不通 | 地址拼写、端口、DNS 解析、防火墙规则 |
| 隧道建立后返回 403 / 401 | 代理已生效,目标站点拒绝 | 目标权限、请求头、访问频率与出口地址限制 |
| 内网或 localhost 请求被代理转发 | no_proxy 名单问题 | 分隔符、域名后缀,确认 localhost 与 127.0.0.1 都在名单内 |

排查时先按日志分诊属于代理侧还是目标侧,再回到配置清单逐项核对。
04 一套可复用的排查顺序:从环境到出口逐层确认
遇到代理不生效,不要直接替换代理服务,按下面五步顺序走,先锁定问题落在哪一层:
- 先看环境里到底有什么。Linux、macOS 下执行
env | grep -i proxy;PowerShell 下用Get-ChildItem Env: | Where-Object Name -match "proxy"查看。确认变量真的存在,再逐个核对变量名和代理值。 - 对齐目标协议。目标地址是
http://还是https://,对应变量是否都已配置;整套变量建议统一写成小写,减少跨工具兼容问题。 - 用
-v看连接行。日志中出现Establish HTTP proxy tunnel to ...和CONNECT,说明请求经过了代理;Trying后面直接是目标主机,说明代理没有生效。 - 分诊两侧。407、代理域名解析失败、端口拒绝属于代理侧问题;隧道返回
200 Connection established之后再出现 401、403 或超时,属于目标站点或中间网络路径问题。 - 对比出口 IP。先直连访问一次 IP 查询接口,再走代理访问一次;两次返回不同出口地址,才算代理真正生效。只看命令没有报错,不能证明请求走了代理。
补充一个常见误判:不要用 ping 判断代理是否工作。ping 走 ICMP 协议,HTTP、SOCKS 代理是应用层通道,二者不在同一层;ping 不通代理地址,并不代表 HTTP 代理不可用。
写在最后:特殊规则背后是一致性优先
回到开头的问题:curl 代理不生效,先别急着怪网络和代理服务。按优先级排查命令行参数、配置文件和环境变量;按目标协议对齐变量名;再用日志区分问题出在代理侧还是目标侧。
那条特殊规则值得记住:http_proxy 只认小写,不是工具写错了,而是一条延续多年的安全防护;其余变量大小写均可,但全小写是最不容易踩坑的习惯。代理配置是否生效,最终要靠日志和出口地址双重验证,而不是命令跑完没报错就算成功。
