生产环境严禁运行composer install,根本原因在于必须从执行链上彻底掐断——构建阶段完成依赖安装并生成只读产物,生产机不存composer二进制、无执行权限,杜绝任何动态安装可能。

生产环境不能运行任何 install/update 脚本,根本原因不是“要不要禁”,而是“不能给执行机会”
生产机上压根不该存在可执行的 composer 命令,更不该让 PHP-FPM 或部署脚本有权限调用它。所谓“禁止特定脚本”,本质是误判问题——你真正要防的不是某个 post-install-cmd,而是整个 composer install 流程在生产机上被触发。一旦允许执行,--no-scripts 只是补丁,不是防护。
composer install --no-scripts 只跳过 scripts 字段,不等于安全
--no-scripts 会跳过 composer.json 中 scripts 段定义的所有钩子,比如 post-install-cmd、pre-autoload-dump,但它不跳过 autoload 文件生成、不跳过 classmap 写入、不跳过 vendor/ 目录创建。这些操作仍需写磁盘,而生产环境的 www-data 用户通常无权写 vendor/ 或 composer/cache,直接报 Permission denied。
- 常见错误:在 Docker 部署中加了
--no-scripts,却忘了--no-dev,结果phpunit还是被装进去了 - 更隐蔽的问题:某些包(如
laravel/framework)依赖post-autoload-dump生成优化类映射;禁用后虽能跑,但类加载变慢、opcache 效率下降 - 注意:
--no-scripts不影响插件(如symfony/flex)逻辑,插件仍可能执行任意代码——除非你同时配了"allow-plugins": {}或用--no-plugins
真要“禁用特定脚本”,只能改 composer.json,且仅限构建阶段
Composer 原生不支持按名称禁用单个脚本,--no-scripts 是全局开关。若你确认某条脚本(比如 npm run build)在生产环境纯属冗余,又不想全局禁用,唯一可靠方式是临时修改 composer.json:
- 手动删掉或注释掉对应字段:
"post-install-cmd": "npm run build" - 自动化场景可用
jq处理:jq 'del(.scripts."post-install-cmd")' composer.json > composer.json.tmp && mv composer.json.tmp composer.json - 切记:该操作必须在 CI 构建机或本地干净环境中完成,产出的
vendor/才同步上线;绝不能在生产机上现场改、现场装
最简、最稳的做法:构建阶段装好,生产机只读
把所有“禁止”动作前置到构建环节,生产机彻底剥离 composer 依赖:
- CI 中固定命令:
composer install --no-dev --optimize-autoloader --classmap-authoritative --no-scripts --no-interaction - 构建产物只含
vendor/、autoload.php和composer.lock,不带composer.json也没关系 - 上线前删掉生产机上的
composer二进制,或chmod -x $(which composer),从执行链上掐断 - 如果项目用了
allow-plugins白名单,确保生产镜像里没配"hirak/prestissimo": true这类已淘汰插件——它们可能绕过--no-scripts行为
真正容易被忽略的点是:即使你加了所有参数,只要 vendor/ 是在生产机上生成的,它的文件权限、umask、SELinux 上下文就不可控。构建机和生产机的用户/组不一致,chown -R www-data:www-data vendor/ 不是补救,而是暴露了流程缺陷。


















