ThinkPHP 6 在宝塔反向代理下多语言切换失效,根本原因是 Nginx 终止 HTTPS 后 PHP 收不到 $_SERVER['HTTPS'],导致框架自动禁用 Cookie 的 secure 标志,浏览器因不满足安全策略而丢弃 think_lang Cookie;须在 public/index.php 开头强制设置 $_SERVER['HTTPS'] = 'on' 或配置 trust_proxy 并透传 X-Forwarded-Proto。

宝塔环境下 ThinkPHP 6 多语言切换失效,大概率是 Cookie 写入失败导致 think_lang 没存上,Lang 中间件读不到值,直接回退到默认语言——这不是代码写错了,而是 HTTPS + 反向代理场景下 Cookie 的 secure 标志被框架悄悄禁用了。
为什么 cookie('think_lang', 'en-us') 看似执行了却没生效
ThinkPHP 默认通过 $_SERVER['HTTPS'] 判断是否启用 secure,而宝塔通常用 Nginx 终止 HTTPS,转发 HTTP 请求给 PHP-FPM。此时 $_SERVER['HTTPS'] 为空,框架自动把 secure => true 降级为 false,但你的浏览器只接受 secure Cookie(因为站点走 HTTPS),结果就是:Cookie 被 PHP 发出了,但浏览器直接丢弃,DevTools 的 Application → Cookies 里根本看不到 think_lang。
- 检查方式:在控制器里加
dump($_COOKIE['think_lang']);,返回null;再看浏览器 DevTools,think_lang条目缺失或没有 Secure 标志 - 不是中间件没注册,也不是路径写错——Lang 中间件能跑,只是读不到 Cookie
- 本地开发(HTTP)正常,一上宝塔就失效,基本锁定为反向代理协议识别问题
宝塔 Nginx 下必须强制声明 HTTPS 上下文
不能依赖运行时检测,得在请求最开头就“骗过”框架。打开 public/index.php,在 <?php 标签后第一行插入:
if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') {
$_SERVER['HTTPS'] = 'on';
}
- 这个判断比 ThinkPHP 的任何初始化都早,确保后续所有 Cookie 操作(包括
cookie()助手函数、Lang 中间件读取)都能拿到正确的协议标识 - 不要放错位置:必须在
require __DIR__ . '/../vendor/autoload.php';之前 - 别用
Request::isSsl()做条件赋值——它内部逻辑和 Cookie 发送不同步,不可靠
config/app.php 的 cookie 配置必须显式固化
即使加了上面的强制声明,仍需在配置里写死关键参数,防止中间件或手动调用时遗漏:
立即学习“PHP免费学习笔记(深入)”;
'cookie' => [
'prefix' => 'think_',
'expire' => 0,
'path' => '/',
'domain' => '.yourdomain.com', // 注意开头的点号
'secure' => true, // 必须写死,不依赖检测
'httponly' => true,
'samesite' => 'Lax',
],
-
secure => true和httponly => true缺一不可:前者让浏览器只在 HTTPS 下发送,后者防 XSS 读取 -
domain值必须带前导点号(如.example.com),否则子域名(如admin.example.com)无法共享think_lang - 如果插件需要独立语言切换(如微信支付页),必须用
plugin_wechat_lang这类带前缀的键名,避免和主站冲突
Lang 中间件注册与加载时机不能错位
Cookie 写进去了,Lang 中间件却没读——说明它没在正确时机执行。检查 app/middleware.php:
- 确认
think\middleware\Lang在数组中,且**未被注释**;多应用模式下,要检查对应应用目录下的middleware.php - 顺序很重要:Lang 中间件必须排在
SessionInit之后、路由解析之前,否则$request->cookie()可能还为空 - 切勿在控制器里先调
Lang::set('en-us')再期望 Lang 中间件“覆盖”——它只在初始化阶段读一次 Cookie,之后改$_COOKIE或调cookie()都不会触发重载
最隐蔽的问题往往不在语言包本身,而在 Cookie 是否真正落到了浏览器里:先盯死 DevTools 的 Cookie 列表有没有 think_lang、有没有 Secure 标志、Domain 是否匹配,再查 PHP 日志有没有写入失败提示,最后才动语言包路径和中间件逻辑。



















