Composer install 报错头号原因是下载源异常:镜像地址错误(如缺末尾斜杠)、使用已停服源(如phpcomposer.com)、项目级repositories字段静默覆盖全局配置、composer.lock残留旧dist.url,或CA证书过期导致TLS失败。

下载源异常是 composer install 报错的头号原因,不是网络慢、不是 PHP 坏了,而是 Composer 正在请求一个根本不存在或已失效的 URL——比如镜像地址少了个斜杠、用了停服的源、或者项目级配置偷偷覆盖了全局设置。
镜像地址写错导致 404 或 TLS 错误
最常见的错误是把 https://mirrors.aliyun.com/composer/ 写成 https://mirrors.aliyun.com/composer(缺末尾 /),结果 Composer 拼出 /composerpackages.json 这种非法路径,直接返回 404;或者用上了已下线的源(如 phpcomposer.com、laravel-china.org),HTTP 返回 HTML 页面而非 JSON,导致解析失败。
- 验证是否真配对:运行
composer config -g repo.packagist,输出必须是完整 JSON,例如{"type": "composer", "url": "https://packagist.mirrors.ustc.edu.cn/"};空、null、或显示https://packagist.org都说明没生效 - 当前稳定可用的国内源只有两个:
https://packagist.mirrors.ustc.edu.cn/(中科大)、https://mirrors.cloud.tencent.com/composer/(腾讯云);阿里云和 Laravel China 镜像已在 2025–2026 年陆续停用 - 手动测源是否存活:
curl -I https://packagist.mirrors.ustc.edu.cn/packages.json必须秒回HTTP/2 200;返回 404、HTML 或超时,就别再试了
项目级 repositories 字段静默覆盖全局配置
只要项目根目录的 composer.json 里有 "repositories" 字段(哪怕只是空数组 []),全局镜像就彻底失效,Composer 会 fallback 到 packagist.org,但你完全看不到提示。
- 检查是否存在干扰:
grep -A5 '"repositories"' composer.json - 临时禁用它:
composer config --unset repositories(无-g) - 想保留自定义源又用国内镜像?把镜像地址显式写进项目
composer.json的repositories里,而不是依赖全局配置
缓存和 lock 文件残留旧源地址
composer.lock 里硬编码了每个包的 dist.url,如果之前用的是已停服的源,换镜像后不删它,Composer 就会继续尝试下载那个失效的 URL,报 file could not be downloaded。
- 必须清除三样东西:
vendor/、composer.lock、composer clear-cache - Windows 用户额外清一下:
%LOCALAPPDATA%\Composer\cache - 验证是否真走新源:
composer install -vvv 2>&1 | grep -i "downloading\|host" | head -n 3,第一行出现的域名才是实际请求目标
CA 证书过期引发 cURL error 60
这不是源的问题,但现象高度相似:报 certificate verify failed 或卡在 Loading composer repositories。常见于 Docker 容器、CentOS 虚拟机、或系统时间偏差 >5 分钟的 macOS。
- 查证书路径:
php -r "print_r(openssl_get_cert_locations());",重点关注default_cert_file - 若为空或路径不存在,下载最新
cacert.pem(从curl.se),然后在php.ini中设curl.cainfo = "/full/path/to/cacert.pem" - 别用
disable-tls true长期掩盖——它绕过所有 HTTPS 验证,存在中间人风险
最易被忽略的一点:你以为在用镜像,其实 composer.lock 里还锁着 packagist.org 的 dist 地址;你以为删了 vendor 就够,却忘了 composer clear-cache 才真正刷新元数据缓存。源一换,整套缓存链都得重置,不然永远在旧路上打转。











