根本原因是php未加载有效ca证书文件,需运行php -r "print_r(openssl_get_cert_locations());"确认default_cert_file路径,下载官方cacert.pem并同步配置php.ini中openssl.cafile与curl.cainfo为同一绝对路径。

HTTPS协议失败根本不是“镜像没换对”或“网络差”,而是PHP底层SSL证书验证链断裂——必须定位并修复openssl.cafile和curl.cainfo指向的CA证书文件。
先确认PHP到底在用哪个证书文件
别猜路径,让PHP自己说:php -r "print_r(openssl_get_cert_locations());"。重点看两个字段:
-
default_cert_file:PHP默认尝试读的路径,如果为空、不存在、或文件大小为0,就是问题根源 -
ini_cafile:来自php.ini的显式配置,如果它和default_cert_file不一致,说明php.ini没生效或加载了错误的配置文件
常见失效路径包括:/etc/ssl/certs/ca-certificates.crt(旧版Ubuntu)、C:/php/extras/ssl/cacert.pem(XAMPP自带的老包)、/usr/local/etc/php/cacert.pem(Homebrew PHP未更新时)。
openssl.cafile 和 curl.cainfo 必须同时设、路径完全一致
Composer依赖PHP的cURL和OpenSSL扩展发起HTTPS请求,这两个扩展各自读一个配置项:
-
openssl.cafile控制OpenSSL层(如file_get_contents、stream_context_create) -
curl.cainfo控制cURL层(Composer默认HTTP客户端底层)
只配一个?大概率仍有报错;路径写错(比如漏.pem、用相对路径、Windows单反斜杠被当转义符)?照样失败。正确做法是:
工作流任务拆解与模型编排技能。使用场景:(1) 收到复杂工作任务需要拆解为可执行步骤,(2) 需要为不同步骤选择最合适的模型,(3) 需要跟踪工作流进度和模型使用情况,(4) 长时间任务卡住需要问题诊断和解决方案。
- 下载最新
cacert.pem(来源必须是curl.se 官方),存到稳定绝对路径,例如:/usr/local/etc/php/cacert.pem或C:/php/extras/ssl/cacert.pem - 在
php.ini中写两行,路径必须完全相同:openssl.cafile="/usr/local/etc/php/cacert.pem" curl.cainfo="/usr/local/etc/php/cacert.pem"
- Windows下路径用正斜杠
/或双反斜杠\,别用单反斜杠
别信composer config --global cafile,它基本无效
很多人试过composer config -g cafile "/path/to/cacert.pem",但依然报cURL error 60: SSL certificate problem——因为该配置只影响Composer自封装的HTTP客户端(如php-http驱动),而实际触发错误的是底层cURL或OpenSSL调用,根本不走这个配置。
验证是否修好:运行php -r "echo file_get_contents('https://mirrors.aliyun.com/composer/packages.json') ?: 'fail';"。如果输出JSON内容,说明证书链已通;如果仍报错,说明php.ini没重载或路径不对。
系统代理劫持TLS流量也会导致SSL handshake failed
中文Windows/macOS上常见Clash、Surge等本地代理工具启用MITM模式,它们生成自签名根证书,但PHP OpenSSL并不信任它。现象是:curl -v https://packagist.org能通,但composer install卡住或报SSL routines:ssl3_read_bytes:sslv3 alert handshake failure。
临时验证是否为代理引起:HTTP_PROXY= HTTPS_PROXY= composer install(Linux/macOS)或set HTTP_PROXY= && set HTTPS_PROXY= && composer install(Windows CMD)。如果此时成功,说明问题确由代理引起——此时应把代理导出的根证书合并进你刚配置的cacert.pem,而不是关掉代理。
真正麻烦的从来不是“证书过期”,而是PHP加载了一个空文件、一个404路径、或一个被代理污染却没被识别的证书集合——它不会报错,只会静默拒绝所有HTTPS连接。










