composer默认走https导致私有仓库连接失败,根本原因是其内部协议归一化机制优先尝试https并回退,需通过composer config -g github-protocols ssh强制ssh,或在repositories中显式声明type为vcs且url以git@开头。

默认走 HTTPS,但私有仓库或公司内网常需切到 SSH(git@)才能拉取;不改配置会卡在认证失败或 401。
为什么 composer install 总是走 HTTPS 却连不上私有 Git 仓库
Composer 默认用 HTTPS 协议克隆 Git 仓库(比如 https://github.com/vendor/pkg.git),哪怕 composer.json 里写的是 SSH 地址(git@github.com:vendor/pkg.git)。这是因为它内部做了协议归一化——只要没显式禁用 HTTPS fallback,就会优先尝试 HTTPS。
- 现象:执行
composer update时卡住,或报错Failed to download vendor/pkg: The "https://api.github.com/repos/vendor/pkg/zipball/..." file could not be downloaded (HTTP/2 401) - 根本原因:HTTPS 路径需要 token 或密码,而 SSH 路径依赖本地
~/.ssh/id_rsa和 known_hosts 配置 - 关键点:不是
composer.json写什么就用什么,而是由全局 Git 行为 + Composer 的gitlab-domains/github-expose等隐式规则共同决定
用 composer config 强制所有 Git 包走 SSH
最直接有效的方式是告诉 Composer:所有 Git 类型的 VCS 包,一律用 SSH 协议解析。这不是改 Git 全局设置,而是 Composer 自己的 URL 重写规则。
- 运行命令:
composer config -g github-protocols ssh - 效果:后续所有
git@、github.com、gitlab.com域名下的包,Composer 会把https://github.com/...自动转成git@github.com:... - 注意:仅影响 GitHub/GitLab 官方域名;自建 Git 服务器(如
git.example.com)需额外配gitlab-domains或用composer config -g repo.packagist.org.allow_ssl_downgrade true配合 SSH known_hosts - 验证是否生效:执行
composer config -g github-protocols,应输出["ssh"]
临时切换单个包的协议(不用改全局)
某些场景下只想让某个私有包走 SSH,其余保持 HTTPS——这时不该动全局配置,而是靠 repositories 显式声明类型和 URL。
- 在
composer.json中添加:
"repositories": [
{
"type": "vcs",
"url": "git@git.example.com:team/private-pkg.git"
}
]
- 必须满足两个条件才生效:
"type": "vcs"+ URL 以git@开头;若写成https://,即使 type 是 vcs,Composer 仍可能回退到 HTTPS - 风险点:如果该 Git 服务器未被
ssh-add管理,或~/.ssh/config没配好 Host 别名,会直接报Permission denied (publickey),且错误提示藏在 verbose 日志里(加-vvv才能看到) - 不推荐用
git+ssh://格式——Composer 对它的支持不稳定,容易解析失败
Git 全局设置对 Composer 的实际影响有限
很多人试过 git config --global url."git@github.com:".insteadOf "https://github.com/",发现对 Composer 没用。这是因为 Composer 不调用 git clone 命令行,而是用自己封装的 GitDownloader 类,只读取部分 Git 配置(如 core.sshCommand),不走 insteadOf 规则。
- 唯一有用的 Git 全局设置是:
git config --global core.sshCommand "ssh -o StrictHostKeyChecking=no"(仅用于跳过首次连接确认,生产环境慎用) -
git config --global url.*.insteadOf只影响你手动执行git clone,不影响 Composer 的包下载流程 - 真正起作用的,永远是 Composer 自己的配置项(
github-protocols、gitlab-domains)和repositories声明
协议切换的核心不在 Git 命令本身,而在 Composer 解析包 URL 的那一刻——它按固定顺序查配置、匹配域名、决定用哪种 scheme 构造最终地址。漏掉 github-protocols 这个开关,或者误以为 insteadOf 能接管 Composer 流程,是最常见的两个盲区。











