ThinkPHP 6 需配置反向代理透传 X-Forwarded-Proto 头并启用 trust_proxy,才能正确识别 HTTPS;同时需设置 Cookie secure=true、修复混合内容问题。

ThinkPHP 6 如何正确识别 HTTPS 协议
默认情况下,Request::scheme() 或 $request->scheme() 返回 http,哪怕你用 Nginx/Apache 反向代理了 HTTPS——因为 PHP-CGI 进程根本收不到原始的 SSL 信息。
必须让 Web 服务器把协议头透传给 PHP。Nginx 示例配置关键行:
proxy_set_header X-Forwarded-Proto $scheme;
Apache 则需启用 mod_headers 并加:
RequestHeader set X-Forwarded-Proto "https" env=HTTPS
之后在 ThinkPHP 中启用信任代理头(否则忽略):
立即学习“PHP免费学习笔记(深入)”;
- 修改
config/app.php,确保'trust_proxy' => true - 或显式设置信任的代理 IP 段:
'trust_proxy' => ['127.0.0.1', '10.0.0.0/8'] - 不设
trust_proxy会导致Request::isSsl()始终返回false
强制跳转 HTTPS 的安全写法(非 .htaccess 或 Nginx 全局重定向)
在应用层做跳转更可控,尤其当你的入口可能被多层代理、CDN 或负载均衡中转时,直接判断 X-Forwarded-Proto 比依赖 $_SERVER['HTTPS'] 可靠得多。
推荐在中间件中统一处理:
public function handle($request, \Closure $next)
{
if (!$request->isSsl() && $request->header('x-forwarded-proto') !== 'https') {
return redirect()->to('https://' . $request->host() . $request->url());
}
return $next($request);
}
注意点:
- 不要只检查
$_SERVER['HTTPS'] === 'on',云环境几乎无效 - 避免硬编码域名,用
$request->host()动态获取,兼容泛域名和多站点 - 跳转前务必确认
trust_proxy已开启,否则$request->isSsl()不会读取X-Forwarded-Proto
Cookie 的 Secure 和 SameSite 属性必须显式开启
即使全站走 HTTPS,ThinkPHP 默认生成的 Cookie 仍不带 Secure 标志,浏览器可能在 HTTPS 页面发送明文 Cookie——这是典型的安全疏漏。
修改 config/cookie.php:
-
'secure' => true:强制仅通过 HTTPS 传输 Cookie -
'httponly' => true:防 XSS 窃取(已有默认值,确认未被覆盖) -
'samesite' => 'Strict'或'Lax':防 CSRF,推荐Lax兼容性更好 - 如果使用
session,还需同步改config/session.php中的'secure'和'samesite'
漏掉 secure => true 会导致登录态在 HTTPS 页面下无法携带 Cookie,表现为反复跳登录页。
HTTPS 下的资源加载失败:混合内容(Mixed Content)排查
浏览器会直接拦截 http:// 的 JS/CSS/图片请求,控制台报错 Mixed Content: The page at 'https://...' was loaded over HTTPS, but requested an insecure resource 'http://...'。
ThinkPHP 自身不生成外链,但常见于:
- 模板里写死
http://资源地址(如 CDN、统计脚本) -
url()生成链接时未自动适配协议,需确认config/app.php中'url_domain_deploy' => true - 第三方 SDK 初始化时硬编码 HTTP 协议(例如旧版微信 JS-SDK)
- 数据库中存了
http://的图片路径,前端直接输出
修复建议:
- 所有静态资源 URL 改用协议相对路径:
//cdn.example.com/js/app.js - 动态链接统一用
url()或Url::build(),它们会自动继承当前请求协议 - 数据库字段内容需清洗,或在输出前用
str_replace('http://', 'https://', $url)临时兜底(不推荐长期用)
混合内容不是后端能完全拦截的问题,它发生在浏览器渲染阶段,必须从前端源头清理。











