ThinkPHP 默认不防 Host 头攻击,需显式开启 app_host_check 并配置 app_host 白名单;Nginx 层 server_name 限制和兜底 server 块更可靠,反向代理场景需确保 HTTP_HOST 正确透传。

ThinkPHP 的 url_domain_root 和 url_host 配置不防 Host 头攻击
直接说结论:ThinkPHP 默认不校验 Host 头,url_domain_root 仅用于 URL 生成(比如 url() 函数拼域名),url_host 也只是控制是否在生成 URL 时带上 host。它们对请求入口的 Host 头完全不做拦截或比对——攻击者伪造 Host: evil.com,只要 Web 服务器(如 Nginx/Apache)把该请求转给 PHP,ThinkPHP 就照常执行。
真正起作用的是 app_host + app_host_check
ThinkPHP 提供了可信域名白名单机制,但必须显式开启且正确配置:
-
app_host_check必须设为true(默认是false) -
app_host填写允许的域名数组,例如['example.com', 'www.example.com'];支持泛域名写法'*.example.com',但注意它只匹配一级子域(api.example.com可以,dev.api.example.com不行) - 校验发生在应用初始化早期,在路由解析前,所以能拦住绝大多数非法 Host 请求
- 如果 Host 不匹配,会直接抛出
think\exception\HttpException,状态码 400,不会进入控制器
示例配置(config/app.php):
'app_host_check' => true, 'app_host' => ['example.com', 'www.example.com'],
Web 服务器层更可靠:Nginx 的 server_name 限制
ThinkPHP 的 app_host_check 是应用层防护,有绕过可能(比如通过 CLI 或某些 SAPI 环境)。生产环境强烈建议在 Nginx 层兜底:
立即学习“PHP免费学习笔记(深入)”;
- 删掉默认的
server块中无server_name的“通配”配置 - 每个合法域名单独写一个
server块,并明确指定server_name example.com www.example.com - 加一个兜底
server块,监听 80/443,但不设server_name或设为无效值(如server_name _;),里面返回 444(Nginx 关闭连接)或 403
这样连请求都进不到 PHP,Host 头攻击根本没机会触发 ThinkPHP。
别忽略 HTTPS 场景下的 HTTP_HOST 和 SERVER_NAME 差异
当使用反向代理(如 CDN、SLB)时,真实 Host 可能被覆盖。常见问题:
- Nginx 透传 Host 头需显式配置:
proxy_set_header Host $host;(不是$http_host,后者可能带端口) - ThinkPHP 默认读取
$_SERVER['HTTP_HOST']做校验;若代理把 Host 改成内部地址(如127.0.0.1:8080),app_host_check会失败 - 此时应配合
trust_client_ip和trusted_proxy配置,并确保代理设置X-Forwarded-Host,再在中间件里手动覆写$_SERVER['HTTP_HOST']
这种链路下,app_host_check 是否生效,取决于你最终让 ThinkPHP 看到的是哪个 Host 值——这点很容易被忽略,一配完就以为万事大吉。



















