朕喜技术(ZhenxiJS)
2026.09.06
1 分钟阅读
curl 连不上 localhost:HTTP_PROXY 环境变量劫持本地请求(Connection timed out)
2026-09-06 | curl 8.x | http_proxy / HTTP_PROXY / NO_PROXY / no_proxy | Docker 容器 | 连接超时
一、问题背景
在配置了 HTTP 代理的开发环境或 CI 容器中,环境变量里被注入了不可达的代理地址:
http_proxy=http://192.168.1.100:3128
https_proxy=http://192.168.1.100:3128
本地启动了一个 HTTP 服务监听 0.0.0.0:8080,用 curl 访问却超时失败:
curl http://localhost:8080/
# → curl: (28) Connection timed out after 3002 milliseconds
而浏览器访问同地址正常、服务本身也正常运行 —— 只有 curl 连不上。
二、复现步骤
# 1. 启动复现环境(Docker Compose)
cd scenario/2026-09-06-0000/
docker compose up -d --build
# 2. 进入容器
docker compose exec app bash
# 3. 查看代理环境变量(发现 http_proxy 指向不可达代理)
env | grep -i proxy
# 4. 用 --noproxy 绕过代理 → 服务正常
curl --noproxy '*' http://localhost:8080/
# → Hello from local service!
# 5. 普通 curl → 超时失败
curl -s --connect-timeout 3 http://localhost:8080/
# exit code: 28 (Connection timed out)
# 6. verbose 观察根因
curl -v --connect-timeout 3 http://localhost:8080/ 2>&1
# * Uses proxy env variable http_proxy == 'http://192.168.1.100:3128'
# * Trying 192.168.1.100:3128...
三、根本原因
curl 会读取小写 http_proxy 环境变量作为默认 HTTP 代理设置。
当该变量指向一个不可达的代理服务器时,curl 访问任何 HTTP 地址(包括 localhost、127.0.0.1)都会先尝试连接代理 → 代理不可达 → 连接超时 → 请求失败。
关键陷阱:curl 8.x 只读小写 http_proxy,大写 HTTP_PROXY 会被忽略!(安全设计,防止 CGI 环境 HTTP 头注入。)所以在 docker-compose.yml 中只设大写变量、不设小写变量时问题不会触发;排查时必须同时检查大小写两种形式。
四、解决办法
方案 A:临时绕过代理(适合快速验证)
# 当前命令跳过所有代理
curl --noproxy '*' http://localhost:8080/
# 只对 localhost 绕过
curl --noproxy localhost http://localhost:8080/
# 临时清空 http_proxy 变量
http_proxy= curl http://localhost:8080/
方案 B:设置 NO_PROXY(彻底修复,推荐)
在 /etc/profile、~/.bashrc、CI 环境变量或 docker-compose.yml 中配置:
export NO_PROXY="localhost,127.0.0.1,::1,.internal,.example.com"
export no_proxy="localhost,127.0.0.1,::1,.internal,.example.com"
同时设置大小写两种形式,以兼容 curl / wget / Python requests / Java / Node.js 等不同工具。
docker-compose.yml 中的示例
services:
app:
environment:
HTTP_PROXY: "http://proxy.example.com:3128"
HTTPS_PROXY: "http://proxy.example.com:3128"
NO_PROXY: "localhost,127.0.0.1,::1,.local"
no_proxy: "localhost,127.0.0.1,::1,.local"
五、验证
# 设置 NO_PROXY 后无参数 curl 直接成功
export NO_PROXY="localhost,127.0.0.1,::1"
curl http://localhost:8080/
# → Hello from local service! Time: ...
六、经验与避坑
- curl 大小写敏感性:curl 8.x 只认小写
http_proxy,大写HTTP_PROXY被忽略。很多排错时只查大写变量、或只在 docker-compose 里设了大写,导致问题根本不复现。 - NO_PROXY 必须同时设置小写
no_proxy:不同工具/库读的变量名大小写不一致。Python requests 读小写no_proxy,Java 读大写NO_PROXY。 - NO_PROXY 支持通配符:
.example.com匹配所有子域名,10.*匹配 IP 段。 - 排错三件套:
env | grep -i proxy→ 看环境变量curl -v URL 2>&1 | head→ verbose 看是否 "Uses proxy"curl --noproxy '*' URL→ 绕过代理测试确认是代理问题
- **旧版 curl(7.x)**行为不同:旧版会读取大写
HTTP_PROXY(非 CGI 环境),新旧兼容应同时设置大小写。 - CI/容器场景:CI 系统(Jenkins/GitLab)常自动注入代理变量,且通常只设小写形式,最易触发此坑。解决应在 CI 管道中显式配置
NO_PROXY。
可复现环境
git checkout 2026-09-06-0000
cd scenario/2026-09-06-0000/
docker compose up -d
docker compose exec app bash
# 进入容器后按第二节「复现步骤」操作
