composer ssl失败95%因php证书路径未配对,需用php --ini和openssl_get_cert_locations()确认default_cert_file是否有效,同时配置curl.cainfo与openssl.cafile为绝对路径指向官方cacert.pem,并重启环境验证。

Composer 报 SSL 连接失败,95% 的情况不是网络被墙、不是 Packagist 挂了,而是 PHP 的 curl.cainfo 或 openssl.cafile 没指向一个真实存在、内容有效、PHP 进程可读的 CA 证书文件。
怎么确认是证书路径没配对
别猜,直接查 PHP 自己认不认证书:
- 运行
php --ini看Loaded Configuration File路径,确保你改的是这个php.ini - 执行
php -r "print_r(openssl_get_cert_locations());",重点看default_cert_file和ini_cafile的值——如果为空、路径不存在、或文件大小为 0,就坐实了问题根源 - 再跑一句
curl -v https://packagist.org/packages.json:如果也报SSL certificate problem或curl error 60,说明是底层 cURL/SSL 层的问题,和 Composer 配置无关
必须同时配 curl.cainfo 和 openssl.cafile
只设一个,大概率还是失败。因为 Composer 内部不同组件走的路径不同:
-
curl.cainfo控制 PHP 的 cURL 扩展(比如curl_exec()) -
openssl.cafile控制 PHP 流封装器(比如file_get_contents("https://"))、部分插件、甚至某些 Composer 子进程 - 两者都配,才真正兜住所有 HTTPS 出口
在你确认过的 php.ini 末尾加这两行(路径用绝对路径,Windows 用正斜杠或双反斜杠):
工作流任务拆解与模型编排技能。使用场景:(1) 收到复杂工作任务需要拆解为可执行步骤,(2) 需要为不同步骤选择最合适的模型,(3) 需要跟踪工作流进度和模型使用情况,(4) 长时间任务卡住需要问题诊断和解决方案。
curl.cainfo = "/usr/local/etc/php/cacert.pem" openssl.cafile = "/usr/local/etc/php/cacert.pem"
证书文件推荐用官方维护的:curl -sS https://curl.se/ca/cacert.pem -o /usr/local/etc/php/cacert.pem(Linux/macOS),Windows 可保存到 C:/php/extras/ssl/cacert.pem。
为什么 composer config --global cafile 不起作用
它只影响 Composer 自己基于 php-http 的 HTTP 客户端,而绝大多数 SSL 失败发生在更底层的 cURL 或 OpenSSL 扩展层——composer config --global 对那部分完全无效。
- 执行
composer config --global cafile输出路径 ≠ 实际生效路径 -
composer diagnose里显示的CA file行,其实是读取curl.cainfo,不是你配的 global cafile - 想验证是否真生效?改完
php.ini后,关掉终端重开,再跑php -r "var_dump(file_get_contents(ini_get('curl.cainfo')) !== false);",返回bool(true)才算落地
容易被忽略的三个硬坑
修完配置还失败?大概率栽在这三处:
-
CLI 和 Web SAPI 加载不同 php.ini:你在 Apache 的
php.ini里改了,但终端跑composer install用的是 CLI 模式的配置——务必用php --ini确认当前环境 -
杀毒软件或企业代理劫持 HTTPS:哪怕证书路径全对,某些国产杀软会注入自签名根证书并拦截流量。临时关闭“HTTPS 扫描”或把
packagist.org加入白名单 -
路径权限或符号链接失效:Linux/macOS 上
/etc/ssl/certs/ca-certificates.crt常是空链接;Homebrew PHP 默认指向的curl-ca-bundle.crt可能已过期。优先用curl.se提供的cacert.pem并写死绝对路径
最稳的闭环验证不是跑 composer update,而是先确保 php -r "print_r(openssl_get_cert_locations());" 输出的路径和你写的完全一致,且文件可读——这步跳过,后面全是白忙。










