结论:.env文件仅在laravel启动时加载一次,且仅作本地开发入口,不能用于多环境切换;真正起作用的是系统级app_env变量、部署时变量注入及config:cache执行时机控制。

直接说结论:.env 文件只在 Laravel 启动时加载一次,且仅作为本地开发的“入口”,**不能靠它实现多环境切换**;真正起作用的是系统级 APP_ENV 变量 + 部署时变量注入 + config:cache 时机控制。
为什么改了 .env 但 config() 没变
常见错误现象:改完 .env 里的 DB_HOST,php artisan tinker 里 config('database.connections.mysql.host') 还是旧值。
- 根本原因不是没读到文件,而是你已执行过
php artisan config:cache—— 此时所有env()调用已被求值并固化进bootstrap/cache/config.php,后续请求完全绕过.env -
php artisan config:clear只删缓存文件,不重启 PHP 进程;如果你用php artisan serve,必须手动 Ctrl+C 再重启,否则它仍用旧内存里的配置 - 开发中建议禁用缓存:
APP_ENV=local php artisan config:clear,上线前再跑config:cache
APP_ENV=production 但 APP_DEBUG=true 还报错堆栈
这说明 APP_ENV 值没被 Laravel 正确识别,或缓存里固化了旧环境逻辑。
-
APP_ENV必须通过系统级方式设置(如 Nginx 的fastcgi_param APP_ENV production、Docker 的-e APP_ENV=production),不能只写在.env里——因为.env是靠APP_ENV去决定该加载哪个.env.xxx的,顺序不能反 - 执行
config:cache时,当前APP_ENV值会被写死进缓存;如果 CI/CD 脚本里先config:cache再设APP_ENV,缓存里就还是local - 验证方法:
php artisan tinker→ 输入app()->environment(),返回必须是"production",不是"local"或null
部署时怎么安全传入 DB_PASSWORD 这类敏感值
别把 .env 文件 COPY 进 Docker 镜像,也别在 docker-compose.yml 里明文写密码。
- Docker 运行时注入最稳妥:
docker run -e DB_PASSWORD=xxx -e APP_KEY=yyy my-laravel-app;Laravel 5.7+ 会优先读取系统环境变量,覆盖.env里的同名项 - 如果用
--env-file,确保该文件权限为600,且 Web 用户(如www-data)可读;路径要对容器内有效,比如挂载到/var/www/.env.prod - CI/CD 中,用 secrets 注入变量,而不是从
.env.production文件里cat出来——后者容易因换行、空格、$ 符号导致解析失败 -
DB_PASSWORD若含$或:,在 shell 层面传入时必须单引号包裹:'p@ss$word:123',否则会被 shell 提前展开
为什么 config/database.php 里写 env('DB_HOST') 有隐患
表面看没问题,但一旦开了配置缓存,这个调用就被“拍平”成字符串,后续无法动态响应环境变化。
- 更严重的是:某些环境变量值含特殊字符(如
DB_PASSWORD=abc$123),.env解析器在不同 PHP 版本或 shell 下行为不一致,可能截断或报错 - 正确做法是在
config/database.php中做类型转换和 fallback:'host' => (string) env('DB_HOST', '127.0.0.1'),避免 null 导致连接失败 - 复杂逻辑别塞进
env():比如“本地用 sqlite,线上用 mysql”,应该用App::environment('local')判断,而不是靠env('DB_DRIVER')动态拼接数组
最容易被忽略的一点:所有环境变量都应在 config/*.php 中封装默认值并做类型转换,而不是裸调 env();config:cache 不是可选项,是生产环境的强制前提,但它要求变量注入必须发生在缓存生成之前。











