为什么 curl 连接 localhost 而不是远程 URL
在归咎于 VPN 之前,先区分继承的代理、默认 curlrc 文件和真正的目标。三次受控运行都会保持 TLS 校验开启。
如果无论你给的是什么 URL,curl 都试图连接 localhost,那么 VPN 并不是合适的起点。本地 HTTP 或 SOCKS 代理可能就是预期的第一个连接,即使你需要的服务在完全不同的位置。如果该代理已不再运行,那么 curl 还没到达目标,对 localhost 的尝试就已经失败。代理返回了响应,也不能证明目标作出了应答。
按顺序执行这些步骤,并在第一个改变首次连接尝试的步骤处停下。
先读懂 curl 自身的代理规则
curl 会读取协议专用的代理变量,例如 https_proxy 和 HTTPS_PROXY,以及在没有任何协议专用值适用时的 ALL_PROXY。请检查真正启动 curl 的 shell、服务或任务的运行环境,而不是当前终端的环境。一个 CI 作业、一个 systemd 单元和交互式 shell 可能继承三套不同的变量。正是凭可见的 shell 去猜测,才让这类排查白白浪费一下午。
有一个例外值得记住。curl 只接受小写的 http_proxy;大写的 HTTP_PROXY 会被忽略,这就是同一套环境在别的工具里表现不同的原因。
请私下读取这些取值,因为代理 URL 常常带有凭据。
接着检查默认的 curl 配置文件里是否有代理或连接方面的覆盖设置,并确认你实际运行的是哪个可执行文件。shell 别名、函数或包装脚本都可能加上命令行上从不出现的选项。
在开启 TLS 的情况下比较三次调用
三次运行都使用同一个经过批准的诊断目标,以及同一个二进制文件。
curl -v https://service.corp.example/status
curl -q -v https://service.corp.example/status
curl -q --proxy '' -v https://service.corp.example/status
-q 会抑制默认的 curlrc,而且必须作为该行的第一个参数才生效。它并不会移除代理环境变量,这一点常让期待如此的人意外。第三种形式追加一个空的 --proxy 取值,仅对这一次调用覆盖环境所要求的代理。要比较每次输出中的首次连接尝试,而不是最终状态码。
详细输出可能暴露请求头、令牌和代理凭据,因此请留在本地,并在分享前先阅读摘录。
每种结果能排除什么
如果省略默认配置后连接发生变化,那么来源就是配置文件。如果显式停用继承的代理后连接发生变化,那么来源就是环境变量。如果两者都不改变,而 localhost 依然出现,那就检查命令行上的连接覆盖、主机名解析和所选的可执行文件。这三者的修复方式各不相同,而且都不属于路由变更。
自定义库加载或被注入的网络行为属于更后一层,仅有环境变量并不构成证据。
把代理的判断与 VPN 分开
一旦 curl 选择了直连目标,操作系统仍然会为这条连接选路。停用应用层代理并不会停用 VPN 的路由或其过滤策略。只有在获得授权的地方,才进行直连方式的对比。悄悄绕过策略的测试不是有效结果,而是一起事故。
如果确实需要本地代理,应恢复其服务和地址,而不是删除它的设置。
如果只有选定的私网目标需要直连,就添加一条范围很窄且已获批准的排除项,其余流量继续走代理。请保留所选路径上的第一个错误,因为 localhost 拒绝、代理 CONNECT 拒绝、证书失败和授权错误指向四个不同阶段。