朕喜技术(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: ...

六、经验与避坑

  1. curl 大小写敏感性:curl 8.x 只认小写 http_proxy,大写 HTTP_PROXY 被忽略。很多排错时只查大写变量、或只在 docker-compose 里设了大写,导致问题根本不复现。
  2. NO_PROXY 必须同时设置小写 no_proxy:不同工具/库读的变量名大小写不一致。Python requests 读小写 no_proxy,Java 读大写 NO_PROXY。
  3. NO_PROXY 支持通配符:.example.com 匹配所有子域名,10.* 匹配 IP 段。
  4. 排错三件套:
    • env | grep -i proxy → 看环境变量
    • curl -v URL 2>&1 | head → verbose 看是否 "Uses proxy"
    • curl --noproxy '*' URL → 绕过代理测试确认是代理问题
  5. **旧版 curl(7.x)**行为不同:旧版会读取大写 HTTP_PROXY(非 CGI 环境),新旧兼容应同时设置大小写。
  6. 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
# 进入容器后按第二节「复现步骤」操作

版权声明

本文为博主原创文章,遵循 CC BY-NC-SA 4.0 许可协议。 转载请注明出处:http://localhost/blogs/2026-09-06-0000-curl-proxy-hijack

分享这篇文章