离线安装失败的根本原因是Composer默认只信任远程元数据,不验证本地缓存;必须同时满足三个条件:设COMPOSER_DISABLE_NETWORK=1、composer.lock中所有包为dist类型且对应ZIP已按shasum落盘至cache-files-dir指定路径,并显式配置该路径。

离线安装失败,不是没包,是 Composer 不信你有包
离线时 composer install 报 Could not fetch https://repo.packagist.org/packages.json 或静默卡住,根本原因不是缓存目录为空,而是 Composer 默认只信任远程元数据——它压根不查 ~/.composer/cache/files/ 里有没有 ZIP,而是直接按 composer.lock 里的 dist.url 去请求网络。哪怕 vendor 目录全在,只要没显式告诉它“别联网、信本地”,就一定会失败。
关键动作只有两个:COMPOSER_DISABLE_NETWORK=1 是硬开关,必须设;但光设这个不够,还得让 Composer 知道“本地 ZIP 是合法的”。这依赖三个前提同时成立:
-
composer.lock中每个包都含"dist": {"shasum": "xxx", "url": "https://..."}字段(即走--prefer-dist路径生成) - 对应 ZIP 文件已落盘到缓存的
files/子目录,且文件名由 shasum 决定(如monolog/monolog/abc123.zip) - 目标机执行前,用
composer config --global cache-files-dir /path/to/offline-cache显式指向该目录,否则 Composer 忽略它
缓存路径配置错,等于没配
cache-dir 和 cache-files-dir 不是一回事:cache-dir 是顶层缓存根目录(含 files/、repo/、archived/ 等),而离线安装真正读取的是 cache-files-dir 下的 ZIP 包。CI 或内网打包时,只复制 cache-dir 不够,必须确认 files/ 子目录结构完整,且路径被正确注册。
实操要点:
- 联网机先运行
composer config --global cache-files-dir /mnt/nfs/composer-offline-cache,再执行composer install --prefer-dist - 检查缓存目录是否真有内容:进入
/mnt/nfs/composer-offline-cache,看是否存在类似vendor/package/sha256hash.zip的文件 - 离线机上不要只改
cache-dir,必须用cache-files-dir指向同一路径,否则 Composer 仍读默认~/.composer/cache/files/ - Windows 用户注意路径分隔符:用
D:/composer-cache,别用D:\composer-cache,反斜杠会被解析截断
为什么加了 --no-plugins --no-scripts 还会连网
COMPOSER_DISABLE_NETWORK=1 只拦 Composer 主流程的 HTTP 请求,拦不住某些插件或脚本内部发起的调用。比如 hirak/prestissimo 插件会在 post-install 阶段自己拉包,phpstan/extension-installer 可能读远程配置,这些都不受环境变量控制。
所以必须组合使用:
-
--no-plugins:禁用所有全局和项目级插件 -
--no-scripts:跳过post-install-cmd、pre-autoload-dump等钩子 -
--no-autoloader:防止 autoload 生成阶段触发插件读取远程资源(某些插件会在这里做校验) - 如果锁文件里有
"type": "git"条目(如dev-main或私有仓库),--prefer-dist会直接失败——这类包不会进缓存,必须提前换成稳定版约束或改用artifact类型仓库
artifact 仓库是最轻量可控的离线方案
当缓存链路太复杂、版本多变或私有包占比高时,artifact 仓库比依赖全局缓存更可靠:你提供 ZIP 包,Composer 只解压 + 注册 autoload,完全绕过元数据校验和远程 URL 解析。
配置方式简单:
- 把所有依赖 ZIP 放到一个目录,如
/opt/composer-artifacts/ - 在
composer.json里加仓库配置:"repositories": [{"type": "artifact", "url": "/opt/composer-artifacts/"}] - 执行
COMPOSER_DISABLE_NETWORK=1 composer install --no-plugins --no-scripts --prefer-dist - ZIP 文件名必须匹配包名+版本,如
monolog/monolog-2.10.0.zip,Composer 会自动识别并校验 shasum
这个方案不依赖缓存路径、不关心 Composer 版本兼容性、也不怕 lock 文件里混着 git source 条目——唯一要求是你得提前把 ZIP 打好、放对位置、名字写准。


















