composer diagnose 报 cURL error 60 是在提示 PHP 的 OpenSSL 找不到可信 CA 证书文件,或证书过期/损坏;本质是 PHP 运行时证书链配置失效,与网络连通性无关,需通过 php -r "print_r(openssl_get_cert_locations());" 检查 default_cert_file 路径有效性,并确保 openssl.cafile 和 curl.cainfo 配置一致且指向同一有效 PEM 文件。

composer diagnose 报 cURL error 60 到底在报什么
它不是在说“你连不上 Packagist”,而是在告诉你:PHP 的 OpenSSL 找不到可信的 CA 证书文件,或者找到了但内容过期/格式错误。这个错误和代理、DNS、防火墙都无关,纯属 PHP 运行时证书链配置失效。
典型现象:composer diagnose 卡在 Checking HTTPS connectivity 行并标红,后面跟着 cURL error 60: SSL certificate problem;同时 curl -I https://packagist.org 也报同样错误——说明问题出在系统级证书加载环节,不是 Composer 自身逻辑。
- 先跑
php -r "print_r(openssl_get_cert_locations());",重点看default_cert_file路径是否存在、文件是否可读 - 如果路径为空,或指向一个 2021 年前的老
cacert.pem,就是根因 -
composer config -g cafile对 diagnose 完全无效——它只影响 Composer 内部 HTTP 客户端,而 diagnose 直接调用 PHP 的 cURL 扩展
为什么 openssl.cafile 和 curl.cainfo 必须配同一路径
PHP 的 cURL 扩展和 OpenSSL 扩展在 TLS 握手时各自加载证书,但 Composer 的 HTTPS 请求由 cURL 发起,而 openssl_get_cert_locations() 返回的路径又受 openssl.cafile 控制。两者不一致,就会出现“PHP 说自己有证书,cURL 却不用”的错位。
比如 macOS 上 Homebrew PHP 默认只设了 openssl.cafile,但没设 curl.cainfo,结果 curl_setopt(CURLOPT_CAINFO) 拿到空值,回退到系统默认路径(往往不存在)。
- 必须在 CLI 模式生效的
php.ini(用php --ini确认路径)里同时写两行: openssl.cafile = "/usr/local/etc/php/cacert.pem"curl.cainfo = "/usr/local/etc/php/cacert.pem"- 路径必须是绝对路径,不能用
~或环境变量 - 改完要重启终端(CLI)或
php-fpm(Web),否则php -i | grep cainfo仍显示旧值
cURL error 35 和 connection reset by peer 的真正区别
cURL error 35 是 OpenSSL 主动放弃协商,通常因为本地 OpenSSL 版本太老(如 1.0.2k),不支持服务端要求的 TLS 1.3;而 connection reset by peer 是中间设备(防火墙、企业代理、NAT)在收到 Client Hello 后直接发 RST 包,连 Server Hello 都不给机会返回。
两者都会让 composer diagnose 在 HTTPS 连通性检查上失败,但修复路径完全不同。
- 验证方式:用
curl -v https://packagist.org/packages.json,如果停在* TLS handshake且无后续响应,再抓包看是否有 RST —— 有 RST 就是中间设备拦截 - OpenSSL 版本不足:运行
openssl version,若输出含1.0.2,基本可判定不支持 TLS 1.3,需升级 OpenSSL 和 PHP - 别试图用
composer config -g secure-http false绕过——packagist.org 强制 HTTPS 重定向,最终还是卡在 TLS 握手
CI/CD 环境里 composer diagnose 显示 OK 却 install 失败的真相
GitHub Actions 默认 Ubuntu 镜像里 composer diagnose 常报 OK,但 composer install 却卡在 Loading composer repositories。这不是 diagnose 有 bug,而是它默认不加载 shell 环境变量和用户级配置,而 install 过程会触发真实网络请求路径。
典型脱节点:diagnose 用的是 PHP 默认证书路径,而 CI 脚本里可能设置了 GITHUB_TOKEN 或自定义镜像,但没同步更新 PHP 的 openssl.cafile。
- CI 脚本中必须显式设置 INI 项,不能依赖自动探测:
echo "openssl.cafile=/etc/ssl/certs/ca-certificates.crt" >> $(php --ini | grep "Loaded Configuration File" | cut -d' ' -f4) - Alpine 镜像需先
apk add ca-certificates,再确保 PHP 编译时启用了--with-openssl - 别在 diagnose 后直接
&& composer install—— diagnose 失败时整条命令退出,掩盖了 install 阶段的真实错误
关键点在于:diagnose 只暴露环境是否满足 Composer 最小运行条件,但它不模拟实际 install 流程中所有上下文切换。证书路径、扩展启用状态、网络策略这三者哪怕只有一处错位,就会导致 diagnose 通过但 install 卡死。


















