如何设置ThinkPHP强制HTTPS跳转及SSL证书部署并实现301重定向?
- 内容介绍
- 文章标签
- 相关推荐
本文共计910个文字,预计阅读时间需要4分钟。
ThinkPHP 本身不处理 HTTPS 和强制跳转,所有全站 301 跳转 HTTPS必须由 Web 服务器(Nginx/Apache)完成;在 PHP 层硬编码 +3C header('Location: https://...')+ 容易触发 +3C 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 配置有没有写错。
本文共计910个文字,预计阅读时间需要4分钟。
ThinkPHP 本身不处理 HTTPS 和强制跳转,所有全站 301 跳转 HTTPS必须由 Web 服务器(Nginx/Apache)完成;在 PHP 层硬编码 +3C header('Location: https://...')+ 容易触发 +3C 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 配置有没有写错。

