Composer仓库优先级由repositories数组顺序决定,首个返回200且JSON可解析的源即被锁定使用;私有源须置顶,packagist.org需显式禁用,镜像须声明type:"packagist"。

repositories数组顺序就是实际优先级
Composer没有“优先级配置项”,所谓优先级完全由repositories数组中对象的书写顺序决定:从上到下依次发起元数据请求,第一个返回 HTTP 200 + 可解析 JSON 的源即被锁定使用,后续所有源彻底跳过。这不是 fallback,是硬性短路逻辑。
常见错误现象:composer require myorg/private-package 装了 packagist.org 上的同名包,而不是你私有仓库里的版本——根本不是网络延迟导致的“偶然拉错”,而是私有源写在了数组末尾,被官方源抢先命中。
实操建议:
- 私有 Composer 仓库(
type: "composer")必须放在数组最前面,否则同名包必然被覆盖 -
type: "vcs"源(如 Git 地址)不参与全局元数据匹配,只对composer require vendor/name显式调用生效,所以它不和镜像抢顺序,但也不能靠它覆盖laravel/framework这类基础包 - 运行
composer config repositories查看当前真实生效顺序,别依赖注释或记忆
packagist.org 是隐式兜底源,必须显式禁用
默认情况下,Composer 总会把 https://packagist.org 当作隐式兜底源——哪怕你把阿里云镜像写在 repositories 第一位,只要它对某个包返回 404(比如新包尚未同步),Composer 就立刻切回官方源,看起来像“镜像没起作用”。
禁用方式是:在 repositories 数组末尾添加一个独立对象 {"packagist.org": false},不是 "url" 字段,也不是 {"type": "composer", "url": "https://packagist.org/"}。这个对象必须是数组里的一个单独元素,键名必须精确为 packagist.org(不是 packagist 或 packagist.com)。
验证是否生效:composer config repositories 输出中不应出现 https://repo.packagist.org,且应明确看到 "packagist.org": false。
镜像必须显式声明 type: "packagist"
很多人以为在 composer.json 里把 "url": "https://packagist.org" 改成阿里云地址就行,其实不行。Composer 对 type: "packagist" 有特殊处理逻辑:只有明确写了这个 type,它才会启用 packagist 协议的完整索引能力(比如支持模糊搜索、自动补全包名)。随便换个 URL 而不带 type,会被当成普通 type: "composer" 仓库,功能残缺,还可能漏包。
正确写法示例:
{
"repositories": [
{ "type": "composer", "url": "https://pkg.mycompany.com" },
{ "type": "packagist", "url": "https://mirrors.aliyun.com/composer/" },
{ "packagist.org": false }
]
}
注意:type: "packagist" 镜像必须放在私有源之后、{"packagist.org": false} 之前;若混用 type: "composer" 和 type: "packagist",后者将无法触发完整索引逻辑。
404 是唯一触发切换的响应码
Composer 对错误的处理逻辑高度差异化:只有明确的 HTTP 404(包不存在)才会尝试下一个源;DNS 失败、连接超时(默认 30 秒)、502、500 等一律报错退出,根本不会查第二源。
别指望“第一个镜像慢就自动切第二个”——只要它能返回 packages.json(哪怕不含你要的包),第二个镜像就永远不会被访问。
生产环境堆砌多个镜像源不提升可用性,反而增加不可控延迟;加第二个镜像只在一种场景有用:第一个镜像明确返回 404,且你确认该包确实在第二个镜像里。
容易被忽略的一点:换镜像后必须删掉 vendor 和 composer.lock 再跑 composer install。镜像只影响下载行为,不影响 lock 文件内容;如果 lock 里记的是 packagist.org 的 hash,换镜像也没用。











