Composer下载慢的根本原因是直连packagist.org导致DNS解析慢、TLS握手卡、首字节延迟高,必须配置国内镜像源(如阿里云https://mirrors.aliyun.com/composer/)并严格满足三要素:键名为repo.packagist、type值为composer、URL以https://开头且结尾带/,再通过composer config -g repo.packagist验证输出为完整JSON,否则配置无效。

Composer 用 HTTPS 下载慢,不是协议本身的问题,而是默认直连 packagist.org 导致 DNS 解析慢、TLS 握手卡、首字节延迟高 —— 换镜像源是唯一有效解法,其他调参只是补救。
为什么 HTTPS 下载卡在 Downloading https:// 阶段
这个阶段本质是 Composer 在拉元数据(packages.json)或 ZIP 包,走的是标准 HTTPS 请求链:DNS → TLS 握手 → TCP 连接 → 首字节等待 → body 传输。国内访问 packagist.org 时,前三步就常超时或重试,cURL error 28 或反复 retry 就是典型表现。
-
http.timeout控制整条链路总耗时,默认仅 300 秒,弱网下极易触发 - 但即使你把它设成 1200,若 PHP 的
default_socket_timeout还是 60 秒,底层 socket 会提前掐断连接 - 镜像源(如阿里云)直连 CDN,DNS 和 TLS 路径更短,首字节通常在 200ms 内,比直连快 5–10 倍
必须配对生效的镜像配置三要素
只执行 composer config -g repo.packagist https://mirrors.aliyun.com/composer/ 是不够的,缺一不可:
- 键名必须是
repo.packagist(单数,不是repos.packagist) - 第二个参数必须显式写
composer,表示 type 值:composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/ - URL 必须以
https://开头,且末尾带/;少写会拼出/compositorpackages.json导致 404
验证是否真生效:运行 composer config -g repo.packagist,输出必须是完整 JSON,形如 {"type":"composer","url":"https://mirrors.aliyun.com/composer/"};空、null 或仍是 packagist.org 都说明失败。
HTTPS 下载仍慢?先确认是不是真走镜像
镜像只代理元数据请求(packages.json、provider-*.json),不托管 ZIP 包。所以:
- 如果
composer install -vvv日志里还出现GET https://codeload.github.com/...,说明composer.lock里存的还是原始 dist URL,镜像没起作用 - 必须删掉
vendor/和composer.lock,再跑composer install,让 Composer 重新生成带镜像域名的 dist URL - 项目级
repositories字段(哪怕值是{})会完全屏蔽全局镜像配置,要检查并删掉
企业内网若报 SSL 证书错误,先 curl -I https://mirrors.aliyun.com/composer/packages.json 测试;失败则升级系统 CA 证书库,别直接关 secure-http。
超时参数和 PHP 底层 socket 必须同步调
HTTPS 下载慢 ≠ 只调一个参数就能解决。这是两层叠加问题:
-
http.timeout控制 HTTPS 全周期(DNS + TLS + 首字节 + body),建议设为1200;但它不能写进composer.json的config段,只能用composer config -g http.timeout 1200或环境变量COMPOSER_HTTP_TIMEOUT=1200 -
process-timeout控制unzip、git clone等子进程,和 HTTPS 无关,但大 ZIP 解压可能卡在这儿,建议1800 - PHP CLI 的
default_socket_timeout必须 ≥http.timeout,查法:php -i | grep default_socket_timeout;若为 60,需改 php.ini 或临时加-d default_socket_timeout=1200
最易忽略的是:改了 http.timeout 却忘了调 PHP 底层 timeout,结果还是在 60 秒被掐断 —— 日志里看不到明显报错,只表现为“突然中断后重试”。











