ThinkPHP本身不处理HTTPS强制跳转,必须由Nginx/Apache等Web服务器配置301重定向;依赖$_SERVER['HTTPS']判断不可靠,应优先检查$_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https',并启用TrustProxy中间件及代理IP白名单;Nginx层return 301最安全高效,应用层跳转需严格排除CLI/AJAX/静态资源,并用$request->url(true)生成URL;同时须设置Cookie/Session secure为true、模板URL强制HTTPS、替换所有HTTP硬链接,避免混合内容与登录态丢失。

ThinkPHP 本身不处理 HTTPS 强制跳转,所有「全站 301 跳转 HTTPS」必须由 Web 服务器(Nginx/Apache)完成;在 PHP 层硬加 header('Location: https://...') 容易触发 ERR_TOO_MANY_REDIRECTS、混合内容或 CLI 报错,且无法绕过 CDN/SLB 的协议头清洗问题。
为什么不能只靠 $_SERVER['HTTPS'] === 'on' 判断
这个值在绝大多数生产环境(Nginx + PHP-FPM、阿里云 SLB、Cloudflare、Docker)下为空或 off,因为反向代理默认不设置它。TP6 的 $request->isSsl() 底层也依赖这个变量,不补全代理头就永远不准。
- 真实可信的判断依据是
$_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https',这是代理普遍透传的标准字段 - 若用阿里云/腾讯云负载均衡,需确认已开启「传递客户端真实协议」选项
- CDN(如 Cloudflare)可能清洗掉该 header,需在 CDN 控制台检查或启用「Preserve original host header」类选项
Nginx 配置 301 跳转(推荐首选)
这是最安全、无性能损耗、且与框架完全解耦的方式。不要在 index.php 或中间件里写跳转逻辑。
- 确保站点配置中有一个明确监听
80端口的server块 - 在里面直接写:
return 301 https://$host$request_uri; - 若使用泛域名或多站点,用
$server_name替代$host更稳妥 - 若已启用 CDN,务必关闭其「自动跳转 HTTPS」功能,否则和源站规则叠加导致循环
ThinkPHP 中间件跳转要满足三个前提
仅当无法修改服务器配置时才考虑中间件方案,且必须同时满足:
立即学习“PHP免费学习笔记(深入)”;
- 在
config/app.php中启用代理信任:'trust_proxy' => true,并填入可信代理 IP 段(如['10.0.0.0/8', '172.16.0.0/12']) - 在
app/middleware.php中注册think\middleware\TrustProxy::class,且位置早于自定义跳转中间件 - 跳转逻辑内必须排除非 Web 场景:
if (PHP_SAPI !== 'cli' && !$request->isAjax() && !$request->isSsl())
跳转 URL 务必用 $request->url(true) 生成,不要拼 $_SERVER['REQUEST_URI'] —— 后者会丢失 fragment、编码错误、漏端口。
Cookie 和 URL 生成容易被忽略的坑
即使 Nginx 已 301 跳转成功,应用层仍可能泄露 HTTP 链接或发送明文 Cookie:
-
config/cookie.php中必须设'secure' => true,否则登录态在 HTTPS 页面下无法携带 -
config/session.php同样要设'secure' => true和'samesite' => 'Lax' - 模板中调用
{:url('index/index')}默认按当前请求协议生成,应统一改为{:url('index/index', [], true)}强制 HTTPS - 数据库或配置项里硬编码的
http://链接(如图片地址、回调地址)会导致混合内容警告,必须批量替换
最麻烦的不是跳转本身,而是跳转后所有链接、资源、Cookie、CSRF Token 全部要同步适配 HTTPS —— 这些细节一旦漏掉,浏览器控制台就会报错,用户反复登出,而你还在查 Nginx 配置有没有写错。



















