Composer连接失败:cURL error 60 或 SSL certificate problem
遇到Composer报错,提示SSL证书问题或cURL错误60?这事儿在老旧系统上太常见了。无论是还在服役的CentOS 6,还是停留在macOS 10.12之前的机器,亦或是PHP版本过低的运行环境,问题的根源往往不是证书本身,而是系统底层缺乏对现代TLS协议(尤其是TLS 1.2)的支持。

Composer连接失败:cURL error 60 或 SSL certificate problem
简单来说,这是Composer在尝试与Packagist等仓库通信时,因为旧系统(如CentOS 6、macOS 10.12以下、PHP版本过低)的底层库(OpenSSL/cURL)太老,无法与已普遍采用更高安全标准的远程服务器成功“握手”。
怎么快速验证?跑两行命令看看:php -r "print_r(stream_get_transports());" 如果输出结果里压根找不到 tls 或 ssl 的身影,或者执行 curl --version 发现OpenSSL版本还低于 1.0.1,那基本就可以锁定问题了。
在动手之前,先避开几个常见的“坑”:
- 别急着去手动下载根证书然后配置
openssl.cafile—— 这招对付证书校验失败或许有用,但解决不了根本的协议不匹配问题。 - 也别盲目升级Composer本身(
composer self-update)—— Composer版本再新,也绕不过PHP和cURL底层对TLS协议支持能力的限制。 - 真正的关键动作,是让PHP的cURL扩展强制使用TLS 1.2进行通信,而不是依赖可能失败的自动协议协商。
在 php.ini 中强制指定 TLS 1.2(最稳定方案)
修改PHP配置是治本之策,比临时调整环境变量或Composer设置更彻底,能确保每次调用都生效。首先,找到你命令行模式(CLI)正在使用的那个 php.ini 文件,运行 php --ini 就能看到路径。
打开这个文件,在末尾追加以下配置:
[openssl] openssl.cafile=/etc/ssl/certs/ca-certificates.crt [curl] curl.cainfo=/etc/ssl/certs/ca-certificates.crt
然后,重点是加上这一行(必不可少):
curl.options = 2048 ; CURLOPT_SSLVERSION = CURL_SSLVERSION_TLSv1_2
这里的 2048 是cURL常量 CURLOPT_SSLVERSION 对应的整数值,专门用于指定TLS 1.2。虽然PHP 7.0.7及以上版本可以直接写常量名,但在低版本里,必须使用这个数字。修改完成后,重启一下PHP CLI环境(通常不需要动Web服务器),然后再运行 composer diagnose 检查一下。如果看到“The OpenSSL library is a vailable”且没有SSL错误,那就大功告成了。
临时绕过:用环境变量控制(仅限调试)
如果遇到没有权限修改 php.ini 的情况,比如在共享主机环境或者只是为了临时跑个CI任务,可以用环境变量来强制设置。但记住,这只是权宜之计。
export COMPOSER_CAFILE="/etc/ssl/certs/ca-certificates.crt"
export PHP_INI_SCAN_DIR=""
php -d curl.cainfo=/etc/ssl/certs/ca-certificates.crt \
-d curl.options=2048 \
/usr/bin/composer install
这里有两点需要特别注意:
- 设置
PHP_INI_SCAN_DIR=""是为了防止系统加载其他目录下的ini文件,覆盖掉你的临时配置。 - 必须通过
php -d参数直接传递给PHP进程,光设置环境变量是没用的——因为Composer不会去读取CURLOPT_SSLVERSION这样的环境变量。 - 这种方式在Jenkins、GitLab CI等自动化环境中,可能会因为shell类型(sh vs bash)或子进程继承问题导致变量丢失,所以不建议作为长期解决方案。
为什么不用 --no-secure-http?
可能有人会想到执行 composer config -g secure-http false 来允许HTTP源,从而绕过HTTPS。但现实是,主流的Packagist.org早已全站强制HTTPS,而且Composer 2.x版本默认就禁用了不安全的HTTP源。更重要的是,这完全是在回避问题——即使你换到了私有仓库,只要那个仓库启用了TLS 1.2或更高协议,你照样会卡在原地。
说到底,要彻底解决这类问题,眼光得放在三个地方:PHP编译时链接的OpenSSL库版本、系统cURL的版本,以及PHP运行时是否把 CURLOPT_SSLVERSION 这个参数正确地传递给了底层。很多朋友修改了php.ini后依然失败,往往是因为用了 phpbrew 或 asdf 这类版本管理工具,实际执行的PHP和你以为的那个php.ini,根本不是一回事。检查路径,永远是排查的第一步。
