必须使用 fine-grained GitHub Token,权限至少含 Contents: read-only 和 Packages: read packages;通过 composer config -g github-oauth.github.com ghp_... 写入全局 auth.json,并验证日志中出现 200 OK 响应。

Composer 必须配 GitHub Token 才能稳定拉取依赖,否则大概率遇到 API rate limit exceeded 或 Could not authenticate against github.com 错误——这不是可选项,是当前(2026 年)GitHub API 的强制要求。
怎么生成一个合规的 GitHub Token
必须用 fine-grained token(非 classic),classic 已逐步停用;访问 https://www.php.cn/link/9c450eb90c31bc12f1691f235da5a0cc 创建:
- Token name 填清楚用途,比如
composer-dev - Resource owner 选你自己的账号(不是组织)
- Permissions 至少勾选:
Contents: read-only(等价于旧版repo)、Packages: read packages - 如果项目含私有仓库依赖,额外勾选:
Metadata: read-only(某些 Composer 插件需要) - 生成后立刻复制——页面关闭就再也看不到明文
怎么让 Composer 正确读到这个 Token
优先走全局配置,但要注意路径和优先级:
- 执行命令写入(推荐):
composer config -g github-oauth.github.com ghp_xxx...(ghp_开头才是有效 token) - 该命令会把 token 写进
~/.composer/auth.json(Linux/macOS)或%APPDATA%\Composer\auth.json(Windows),并自动处理 JSON 格式和权限(Linux/macOS 下建议chmod 600 ~/.composer/auth.json) - 别手动编辑
auth.json:容易漏逗号、引号不闭合,导致 Composer 启动失败 - 如果项目根目录存在
auth.json,它优先级最高——CI 环境常用此方式,但本地开发时容易误留,建议ls -a | grep auth检查
为什么配了还是报 403 或卡在 Reading composer.json
常见原因不是 token 无效,而是没被 Composer 实际使用:
- 项目
composer.json中写了"repositories"字段且类型为vcs,会绕过全局认证 → 改用package类型或显式加"options": {"http": {"header": ["Authorization: token ..."]}} - 用了 GitHub Enterprise Server(GHES),域名不是
github.com→ 单独配:composer config -g github-oauth.your-company.internal ghp_xxx - CI 环境(如 GitHub Actions)没用
COMPOSER_AUTH环境变量 → 必须传 JSON 字符串:{"github-oauth":{"github.com":"${{ secrets.GITHUB_TOKEN }}"}},不能只设GITHUB_TOKEN - 本地执行过
composer config github-oauth.github.com xxx(无-g)→ 它会把字符串字面量写进项目级composer.json的config字段,覆盖全局配置,删掉那一段再重试
怎么验证 Token 真正生效了
别只信 composer config -g github-oauth.github.com 输出前几位,要测真实请求:
- 运行
composer clear-cache清掉可能缓存的未认证响应 - 找一个明确依赖 GitHub 私有库或冷门公开库的
composer.json,执行composer install -vvv - 观察日志中是否出现
Downloading https://api.github.com/... (200 OK),而不是(403 Forbidden)或长时间等待 - 若仍失败,加
--no-cache参数排除缓存干扰:composer update --no-cache -vvv
最常被忽略的一点:token 权限改了、过期了、被删了,Composer 不会主动报错,只会静默退回到未认证状态,表现和“没配”一模一样。每次遇到拉取异常,第一反应不应该是调网络或换源,而是先确认 token 是否还在有效期、权限是否完整、是否被更高优先级配置覆盖。


















