.env 的三个必查项

这次部署是 2026 年 9 月 17 号做的。1Panel v2 面板,PHP 8.4.25 跑在容器里,镜像就是 1panel-php-fpm:8.4.25。框架是 ThinkPHP 8.1.4,鉴权用 firebase/php-jwt v7.1.1。MySQL 8.4.11 也在容器里,跟 PHP 挂在同一个容器网络上。

部署完成后打不开,通常有明确报错:502、PHP Fatal、连接被拒。这类问题好查,因为它有故障相。

.env 填错是另一回事。它的典型症状是这样:站点能打开,页面能渲染,接口能响应,但就是不对。或者反过来,所有登录接口全线失败,而界面上只显示一句进不去,看起来像前端 bug。

这类故障最费时间,因为它没有故障相。

下面按「填错会出什么症状」来组织,不按字段清单走。命令里的站点目录我都用变量代替,先在终端执行一次。

bash 复制代码
export SITE_DIR="/opt/1panel/www/sites/<你的域名>/index"

这一篇里的配置项和结论,都来自这次真实部署,字段名和结构原样保留,域名、密码、密钥做了参数化。标着官方的部分来自 Composer 和 PHP-JWT 的发布说明与安全公告,出处随文标注。

.env 为什么总是不存在

它跟 vendor/ 是同一个原因,两件事叠在一起。一是 .gitignore 里写了它,git clone 下来的仓库天然不含。二是部署脚本的 rsync 排除清单里也写了它,同步不会覆盖它。

这两条都对。第二条本身还很有必要,否则每次更新代码都会把服务器上的配置清掉。

结果就是它永远不存在,而且永远不会自己出现。

所以唯一的来源是模板。

bash 复制代码
cd "$SITE_DIR"
cp .example.env .env

但从模板复制,跟配置正确,是两件事。我当时也以为复制过来就算配好了,后来才发现模板是给开发机写的默认值,而你的部署拓扑是容器化的。下面三个值,就卡在这道缝里。

必查项一,DB_HOST,模板给的值就是错的

打开刚复制出来的 .env,会看到这一行。

ini 复制代码
DB_HOST = 127.0.0.1

这在开发机上是对的,在容器部署里一定是错的。

PHP 跑在容器里,而 127.0.0.1 在容器内部指向的是容器自己,那儿没有任何 MySQL 在监听。数据库在另一个容器里,你甚至没把 3306 映射到宿主机,这正是正确的安全做法。

正确值该填 MySQL 容器的名字。

ini 复制代码
DB_HOST = <MySQL 容器名>

这个容器名每台服务器随机生成,1Panel 不会给你一个通用值。所以它必须在上一步就记下来。

bash 复制代码
# 安装 MySQL 后立刻记下容器名
docker ps --format "table {{.Names}}\t{{.Image}}" | grep -i mysql

容器之间能靠名字互通,是因为它们接在同一个 Docker 网络上。1Panel 默认建的那个网络叫 1panel-network。这也是为什么「不要开放 3306 到公网」和「容器名能连上数据库」这两件事可以同时成立。

症状:页面能打开,但数据库连不上。就是那个能打开却不对劲的形态。

核验一遍。

bash 复制代码
cat .env | grep DB_HOST      # 显示的必须是容器名,不是 127.0.0.1

显示的必须是容器名,不是 127.0.0.1。

必查项二,DB_PREFIX 必须留空

模板里这一行是这样的。

ini 复制代码
DB_PREFIX =

它是空的,而它必须保持空。

不少人会按习惯给它填个前缀,比如 tp_,理由是表名统一管理更干净。但在当前的这个项目里,Model 用的是完整表名,也就是 v1_xxx 这种写法。前缀会被再拼一层,变成 tp_v1_xxx,而那张表不存在。

症状:模型一调用就报表不存在。

这类错误的隐蔽性在于,它只在第一次真正查库的时候才暴露。如果某个接口走的是缓存或静态数据,可能过很久才发现。

必查项三,APP_DEBUG 要关,但要留到最后再关

这条有正反两面,两面都重要。

正面:生产必须关。

ini 复制代码
APP_DEBUG = false

开着的话,任何一次报错都会把数据库密码、密钥、完整调用栈渲染到页面上。这是直接泄露凭据。

反面:排障期不要先关。

这一点很多人搞反了。如果你在部署一开始就设成 false,那么之后每一次失败都只剩一句。

复制代码
页面错误,请稍后再试

所有线索都没了,你会从能定位的问题退回靠猜。

所以正确的顺序是这样。部署与排障期设 true,需要看到真实报错与堆栈。验收通过后改回 false,生产不能泄露凭据。

先开,修完,再关。这是一个有始有终的动作,不是一次设置。

顺带一个更省事的做法。项目里预留一个公开、无需登录、且会顺带验证数据库连通性的健康检查接口。排障时它能一次回答两个问题:服务活着吗,数据库通吗。不用靠猜。

想确认 APP_DEBUG 到底关没关,故意触发一次错误,看回显里有没有泄露数据库密码。

硬约束:JWT 密钥必须大于等于 32 字节

这一条是硬性的运行时校验,不是建议。短了会让所有登录接口全线失败。

firebase/php-jwt 从 v7.0.0 起新增了 HMAC 密钥长度校验。官方发布说明给的是按算法分档的下限。HS256 要 32 字节,HS384 要 48 字节,HS512 要 64 字节。

为什么被迫从 6.x 升到 7.x

安全公告 CVE-2025-45769 针对的是 php-jwt v6.11.0 及以前。它归在 CWE-326,加密强度不足。公告指出,库不校验密钥长度。

有个细节值得知道。这个 CVE 在 NVD 上是 Disputed 状态,理由是密钥长度是应用的责任,不是库的责任。但 GitHub Advisory Database 保留了这条公告,编号 GHSA-2x45-7fc3-mxwq。原因是维护者确实发了补丁。结果是 composer audit 会拦截所有 php-jwt v6 的安装。

所以对使用者来说,争论谁对不重要,你装不上 6.x 了。

不足的下场是明确的。

vbnet 复制代码
DomainException: Provided key is too short

症状:所有登录接口失败。小程序端表现为进不去,而前端只会显示一句失败提示,看起来像前端问题。

生成方式,以及模板注释里的那个坑

bash 复制代码
# 用户端 JWT 密钥
openssl rand -hex 32
# 管理员端 JWT 密钥(再执行一次,得到不同的值)
openssl rand -hex 32

模板里的注释写的是「请生成 32 位随机字符串」。这里很容易被 32 这个数字绕进去。

openssl rand -hex N 产出的是 2N 个字符,1 个字节变成 2 个十六进制字符。

-hex 16 产出 32 个字符,长度恰好 32 字节。它能通过校验,但正好压在临界值上,零余量。

-hex 32 产出 64 个字符,也就是 64 字节。达标,而且留足了余量。

所以用 -hex 32,产出的字符串长度是 64 个字符。这两件事不冲突。32 在这两个地方分别指的是随机字节数,和要求的最小字节数。

两份密钥必须是不同的值

ini 复制代码
JWT_SECRET = <第 1 个>
ADMIN_JWT_SECRET = <第 2 个>

用户端与管理员端走两套独立密钥。这是爆炸半径控制,万一其中一份泄露,另一侧不会同时失守。

所以别偷懒把两条命令的输出写成同一个值,那样等于把隔离白做了。把两个值放一起比对,它们不能相同。

收尾,权限收紧

.env 里全是凭据,权限要收。

bash 复制代码
chmod 600 "$SITE_DIR/.env"

改完 .env 后,PHP-FPM 要不要重启取决于它的配置加载方式。多数情况下不需要,.env 是应用层在每次请求时读取的。如果发现改动不生效,再重启容器确认。

还有两个值顺手一起查。DB_CHARSET 要填 utf8mb4,填错了是中文乱码,判断依据就是跟库级字符集保持一致。.env 的权限要收到 600,收错了凭据可以被其他用户读取,用 ls -l .env 看一眼就知道。

但有一点要注意。在 1Panel 里改运行环境配置并保存,有概率触发容器重建而不是重启,那会把你之前补装进容器的依赖库清掉。改动 .env 走文件编辑,不要走面板的那个配置页。

相关推荐
长安米粒贵41 分钟前
接口超时了,为什么重试反而把系统打垮?聊聊超时预算的 4 个误区
后端
程序员Sunday1 小时前
Spring @Transactional 没回滚?按代理调用、异常和传播行为排查
java·后端·spring
付威20231 小时前
我用 100 行核心代码,做了一个能接入飞书的 Hermes 式智能体
人工智能·后端
苏三说技术1 小时前
为什么越来越多人用 ZXing?
后端
Rain的Java大神之路1 小时前
🔥13年Java老兵转型AI Agent:90%的人挂在同一个坑,根本不用学Python!(万字实战复盘,建议收藏)
java·后端·面试
IT小番茄1 小时前
用Codex搭可视化大屏工作流 15个行业场景设计稿看完直接抄作业
后端
by————组态1 小时前
Ricon组态适用领域全景解析:工业制造、能源、市政民生与智慧城市四大场景落地实践
后端·物联网·数学建模·智慧城市·能源·制造·组态