composer 在中文 windows/macos 上因系统代理劫持 tls 流量导致 ssl handshake failed,本质是代理未正确透传 sni 或降级 tls 协议,而非证书问题;需通过关闭代理变量验证、配置 openssl.cafile 或 composer.json 中 cafile 指向代理根证书解决。

Composer 在中文 Windows 或 macOS 系统上因系统级代理(如 Clash、Surge、Proxyman、或某些国产“加速器”)导致 SSL handshake failed,本质不是证书问题,而是代理劫持了 TLS 流量但未正确透传 SNI 或未启用 TLS 1.2+ 协商 —— 这类失败通常表现为 curl error 35、SSL routines:ssl3_read_bytes:sslv3 alert handshake failure 或直接卡在 Updating dependencies 不报错但无响应。
为什么系统代理会让 Composer 失败
Composer 默认使用 PHP 的 stream_context 或 cURL 扩展发起 HTTPS 请求,它会继承系统环境变量(如 HTTP_PROXY、HTTPS_PROXY);但多数本地代理工具对 HTTPS 流量采用「MITM 模式」:它们生成自签名根证书并动态签发中间证书。如果该根证书没被 PHP 的 OpenSSL 信任库识别(即不在 openssl.cafile 或系统 CA store 中),就会在 TLS 握手阶段被拒绝。
更隐蔽的情况是:代理把 TLS 1.3 流量降级成 TLS 1.2,或禁用了某些必需的扩展(如 ALPN、SNI),而 Composer 依赖的 libcurl 版本较旧(尤其 Windows 上 PHP 自带的 curl.dll 常为 7.64.1),无法兼容降级后的握手流程。
- 常见错误信息:
Failed to enable crypto、Unable to locate peer certificate、cURL error 60: SSL certificate problem - 不是证书过期,也不是域名不匹配 ——
curl -v https://packagist.org能通,但composer install卡住,基本可锁定为代理与 PHP/curl 协同问题 - Windows 用户尤其要注意:PHP 安装包自带的 CA bundle(
phpextrassslcacert.pem)可能陈旧,且不会自动更新
临时绕过代理执行 Composer 命令
最快速验证是否为代理导致:显式关闭代理环境变量再运行。
- Windows CMD:
set HTTP_PROXY= && set HTTPS_PROXY= && composer install - macOS/Linux:
HTTP_PROXY= HTTPS_PROXY= composer install - PowerShell:
$env:HTTP_PROXY=""; $env:HTTPS_PROXY=""; composer install
如果此时成功,说明问题确由代理引起。注意:这仅绕过当前命令,不影响全局配置。
让 Composer 正确信任代理的根证书
关键不是禁用代理,而是让 PHP 的 OpenSSL 知道该信谁。代理(如 Clash for Windows)通常会在首次启动时将根证书导出到固定路径,例如:
- Clash:
%LOCALAPPDATA%Clash for Windowscertca.crt(Windows)或~/Library/Caches/Clash for Windows/cert/ca.crt(macOS) - Surge:
~/.surge/cert/ca.crt - 手动导出后,需告诉 PHP 使用这个文件作为 CA bundle
修改 php.ini,找到 openssl.cafile 行,取消注释并指向该证书:
openssl.cafile="C:UsersYourNameAppDataLocalClash for Windowscertca.crt"
然后重启 Web 服务或 CLI PHP 进程。验证方式:php -r "print_r(openssl_get_cert_locations());",确认 default_cert_file 已更新。
强制 Composer 使用系统原生 cURL(跳过 PHP stream wrapper)
某些 PHP 版本的 stream_context 对代理支持不完善。可在 composer.json 顶层加配置,强制走 cURL:
{
"config": {
"secure-http": true,
"http-basic": {},
"use-include-path": false,
"preferred-install": "auto",
"sort-packages": true,
"disable-tls": false,
"cafile": "C:\path\to\your\ca.crt"
}
}
其中 cafile 是可选但推荐项,它会覆盖 openssl.cafile 设置,专用于 Composer 的 HTTPS 请求。路径必须是绝对路径,且反斜杠需双写(Windows)或用正斜杠(macOS/Linux)。
注意:cafile 只影响 Composer 自身请求,不影响 require 的包内代码 —— 那部分仍走 PHP 默认 CA 配置。
真正麻烦的从来不是证书本身,而是那个默默插入 TLS 握手中间、又没把证书链完整透传给 PHP 的代理进程。它不报错,只让你等 —— 等到超时,才给你一个模糊的 “SSL handshake failed”。排查时别急着重装 Composer,先看一眼 HTTPS_PROXY 和 openssl.cafile 是否在打架。











