答案是Nginx配置缺失Content-Type头、URL末尾缺斜杠、gzip_vary未启用或packages.json权限不足,导致Composer无法解析元数据或拒绝请求;需补全location规则、校验权限及auth.json域名一致性。

私有 Composer 仓库用 Nginx 部署时,packages.json 无法被正确读取、composer install 仍回源 packagist.org、或返回 403/404 —— 这些问题几乎都出在 Nginx 配置的细节上,而不是仓库本身没生成。
为什么 curl 能拿到 packages.json,但 composer update 却报错?
Composer 客户端对响应头和路径非常敏感。即使 curl -I https://repo.example.com/packages.json 返回 200,也可能因以下任一原因失败:
- Nginx 未设置
add_header Content-Type application/json;,导致 Composer 解析元数据失败(尤其在 Satis 静态仓库中) - URL 末尾缺少斜杠:
"url": "https://repo.example.com"❌ 必须写成"url": "https://repo.example.com/"✅ - 启用了 gzip 压缩但未配置
gzip_vary on;,部分 Composer 版本会拒绝解压后的 JSON 流 - 使用了
try_files $uri $uri/ =404;却没显式处理/.well-known/composer/(某些私有仓库工具生成该路径)
nginx.conf 中必须加的 location 规则
默认的 root + index 不足以支撑 Composer 协议。至少要补全三类规则:
- 静态资源直出(关键):
location /packages.json { add_header Content-Type application/json; expires 1h; } - dist 包下载支持(Satis 默认用
/dist/目录):location ^~ /dist/ { internal; # 或配 auth_basic 防未授权下载 } - 禁止敏感文件暴露:
location ~ /\.(json|lock|git|hg|svn|yml|yaml)$ { deny all; }
注意:internal 指令必须配合 alias 使用,且不能与 root 混用;若 dist 文件在 /var/www/repo/dist/,对应 alias 应为 alias /var/www/repo/dist/;。
HTTPS + Basic Auth 下 composer install 失败怎么办
Composer 对带认证的 HTTPS 源支持有限,常见现象是卡在 “Loading composer repositories”,最终 timeout。根本原因是它不自动携带 Authorization 请求头。
- 解决方法:在项目级
composer.json的repositories中显式传凭据:"url": "https://user:pass@repo.example.com/"
- 更安全的做法:用
auth.json管理凭证(放在项目根目录或COMPOSER_HOME),格式为:{"http-basic": {"repo.example.com": {"username": "user", "password": "pass"}}} - 若 Nginx 启用了
auth_basic,务必确认 realm 名称与auth.json中域名完全一致(含端口、大小写)
部署后 composer install 仍走外网?先查这三点
不是配置没生效,而是客户端根本没命中你的源:
- 运行
composer config --list | grep repositories,确认输出里只有你定义的私有源,没有残留的packagist.org(漏掉"packagist.org": false是最常见原因) - 执行
composer clear-cache,否则旧缓存可能掩盖新配置 - 用
composer diagnose检查是否提示Warning: The lock file is not up to date with the latest changes in composer.json.—— 这说明composer.lock里还存着 packagist 的包地址,需删 lock 文件后重装
真正容易被忽略的是:Satis 构建后生成的 packages.json 若权限为 600(仅 owner 可读),Nginx worker 进程(如 www-data)将无法读取,直接返回 403 —— 别只盯着配置,先 ls -l 看一眼文件权限。











