Composer install/update卡在SSL handshake是因PHP的cURL或openssl在TLS握手阶段失败,主因包括本地网络TLS支持不稳、代理干扰、DNS/MTU问题或openssl.cafile未正确配置导致证书验证反复重试;需优先检查并配置openssl.cafile,而非仅调大超时。

Composer install/update 卡在 SSL handshake 是什么情况
这不是 Composer 本身的问题,而是 PHP 的 cURL 或 openssl 在建立 HTTPS 连接时,尝试与 Packagist(<a href="https://www.php.cn/link/ec811d0d775adc62776ba80fadd4ed19">https://www.php.cn/link/ec811d0d775adc62776ba80fadd4ed19</a>)或私有仓库握手失败或超时。常见现象是命令行卡在 Downloading <a href="https://www.php.cn/link/ec811d0d775adc62776ba80fadd4ed19/packages.json">https://www.php.cn/link/ec811d0d775adc62776ba80fadd4ed19/packages.json</a> 或类似行,几秒到几十秒后报错 cURL error 28: Operation timed out after 30001 milliseconds。
根本原因通常是本地网络环境对 TLS 1.2+ 握手支持不稳、中间代理干扰、或 DNS/MTU 导致 TCP 分片重传。单纯调大 timeout 只是绕过症状,但对国内用户来说,这是最快速可用的缓解手段。
修改 composer.json 的 config.timeout 不起作用?
config.timeout 控制的是「单个 HTTP 响应读取超时」,不是连接建立或 SSL 握手阶段的超时。它影响的是下载 ZIP 包、解析 JSON 后等待 body 传输完成的时间,对卡在 SSL handshake 完全无效。
真正起作用的是底层 cURL 的两个参数:
-
connect_timeout:TCP 连接建立 + SSL 握手总耗时上限 -
timeout:整个请求(含握手 + 请求发送 + 响应读取)总耗时上限
它们必须通过 Composer 的全局配置或运行时选项注入,不能写在 composer.json 里。
用 composer config 设置全局 connect-timeout
执行以下命令,将连接层超时设为 60 秒(单位是秒,不是毫秒):
composer config -g github-protocols https composer config -g secure-http true composer config -g http-basic.github.com your-token "" composer config -g --global process-timeout 600 composer config -g --global store-auths true
但注意:上面这些都不管用——因为 process-timeout 是 Composer 自己的进程级看门狗,不是 cURL 参数;github-protocols 和 secure-http 也不影响握手。
✅ 正确做法是设置环境变量,强制 cURL 行为:
- Linux/macOS:在 shell 配置中添加
export COMPOSER_PROCESS_TIMEOUT=600(这个其实还是没用)→ 改用:export COMPOSER_HTTP_PROXY=""清空代理再试 - 真正有效的方案是加
-vvv看 curl 命令,然后手动指定:curl_setopt($ch, CURLOPT_CONNECTTIMEOUT_MS, 10000)—— 但你没法改 Composer 源码 - 所以务实解法:用
COMPOSER_HOME下的auth.json配合镜像源,跳过国外握手
最直接有效的方式是换源 + 关闭 SSL 验证(仅限调试):
composer config -g repo.packagist composer https://packagist.phpcomposer.com # 或国内推荐镜像 composer config -g repo.packagist composer https://packagist.laravel-china.org
如果仍需走官方源,可临时关闭证书验证(⚠️仅限排查,勿长期使用):
composer config -g secure-http false # 并确保 php.ini 中 openssl.cafile 已正确配置,否则即使关了 secure-http 也会卡在 verify
PHP 的 openssl.cafile 配置才是关键瓶颈
很多「SSL handshake 超时」实际是「证书链验证失败后重试」导致的假超时。PHP 默认不自带 CA bundle,若 openssl.cafile 未设置或指向错误路径,cURL 会反复尝试不同根证书,拖慢整个握手流程。
检查方法:
php -r "print_r(openssl_get_cert_locations());"
确认 default_cert_file 是否存在且可读。若为空或报错,手动配置:
- 下载 Mozilla CA bundle:
curl -o /usr/local/etc/openssl/cacert.pem https://curl.se/ca/cacert.pem - 在
php.ini中添加:openssl.cafile=/usr/local/etc/openssl/cacert.pem - 重启 PHP CLI(如用 brew php,执行
brew services restart php)
改完后 composer diagnose 应显示 CA file exists: OK,且 composer update -vvv 中的 curl 日志不再出现 SSL certificate problem: unable to get local issuer certificate。
不解决 CA 配置,光调 timeout 只会让失败来得更晚,而不是更快成功。


















