私有源必须置于repositories数组首位且packagist.org须以独立对象{"packagist.org": false}显式禁用并置于末尾,Composer按数组顺序逐个请求,首个返回HTTP 200且JSON可解析的源即被锁定使用,后续全部跳过。

私有源必须放在repositories数组最前面
Composer查包时,会按repositories数组从上到下逐个请求packages.json,第一个返回 HTTP 200 + 可解析 JSON 的源就锁定使用,后续全部跳过。这不是 fallback,是硬性短路逻辑。
常见错误是把私有源写在阿里云镜像或packagist.org后面——哪怕它响应快、数据全,也永远没机会被访问。
- 正确顺序:
{"type": "composer", "url": "https://pkg.myorg.com/"}必须是repositories数组第一项 - 错误写法:把它和
{"packagist.org": false}写反位置,或夹在两个镜像中间 - 验证方式:
composer config repositories输出中,你的私有 URL 应该排在第一行
packagist.org 必须显式禁用且独立置末
默认情况下,Composer 会把 https://repo.packagist.org 当作隐式兜底源。哪怕你把私有源写在第一位,只要它对某个包返回 404(比如新包还没同步),Composer 就立刻切回官方源——看起来像“优先级失效”,其实是兜底行为被触发了。
禁用方式只有一种:在 repositories 数组**末尾**加一个独立对象:
{"packagist.org": false}
这个对象不能嵌套、不能拼错、不能少点:
- ✅ 正确:
{"packagist.org": false} - ❌ 无效:
{"packagist": false}、{"packagist.com": false}、"packagist.org": false(不是独立对象) - ❌ 全局污染:
composer config --global repos.packagist false会破坏其他项目
vcs 类型不参与全局元数据匹配,别指望它抢优先级
如果你配的是 "type": "vcs"(比如 GitLab 地址),它根本不会出现在元数据合并流程里——只在你明确运行 composer require myorg/utils 时才触发拉取。它不抢顺序,也不能覆盖 laravel/framework 这类基础包。
所以想让私有包接管同名包,必须用 "type": "composer",并确保其 packages.json 包含完整、准确的包声明(含 dist 和 source 字段)。
-
"type": "vcs"适合临时调试或单包引入,不适合做主私有源 - URL 必须是合法 Git 地址,结尾带
.git,且仓库根目录有 composer.json - name 字段必须和
require中写的字符串逐字一致:"myorg/utils"≠"MyOrg/utils"
删 lock 文件和 vendor 后重装才真正生效
配置改完不代表立即起效。因为 composer.lock 里记录的是上次安装时实际下载源的 hash,换源后不清理,Composer 仍会照着旧记录去拉包——哪怕源已不可达,也会报错而非换源。
真实生效三步缺一不可:
- 删掉
vendor/目录 - 删掉
composer.lock - 运行
composer install或composer require myorg/private-package
此时日志里应出现类似 Reading composer.json of https://pkg.myorg.com/packages.json,而不是 Reading composer.json of https://repo.packagist.org/packages.json。这才是私有源真正在工作的信号。











