$request->domain()永远拿不到绑定域名,它只解析HTTP_HOST头;真正能取到绑定结果的是路由匹配后的$request->routeInfo()['domain']或['__domain__'],且中间件必须在ParseUrl之后执行。

直接说结论:$request->domain() 永远拿不到你配置的「绑定域名」,它只解析 HTTP_HOST 头;真正能取到绑定结果的,只有路由匹配完成后注入的 $request->routeInfo()['domain'] 或 ['__domain__'],且必须确保中间件执行时机晚于路由解析。
为什么 $request->domain() 返回的不是你绑定的域名
$request->domain() 的行为和路由绑定完全无关——它只是对 $request->host() 做了标准化处理(去端口、转小写、切子域),底层依赖的是原始 $_SERVER['HTTP_HOST']。而 ThinkPHP 的域名绑定(比如 Route::domain('admin.example.com', ...))是在路由调度阶段才执行的逻辑,中间件运行时路由还没开始跑,$request 里自然没有绑定信息。
常见错误现象:
- 在全局中间件里调用
$request->domain(),结果返回localhost:8000或127.0.0.1,而不是配置的admin.example.com - 部署到 Nginx 后,
$request->domain()变成反代服务器内网地址(如php-fpm:9000),因为 Host 头没透传
正确获取绑定域名的两个前提
想拿到实际匹配上的绑定域名,必须同时满足:
- 中间件注册顺序在
think\middleware\ParseUrl之后(即路由解析已完成)——检查app/middleware.php,你的中间件不能排在ParseUrl前面 - 请求确实命中了某条域名绑定规则;否则
$request->routeInfo()里压根没有domain或__domain__键
推荐写法:
if (isset($request->routeInfo()['domain'])) {
$boundDomain = $request->routeInfo()['domain'];
} elseif (isset($request->routeInfo()['__domain__'])) {
$boundDomain = $request->routeInfo()['__domain__'];
} else {
$boundDomain = null;
}
注意:CLI 环境、未命中任何域名路由、或使用了 Route::any() 等泛匹配时,该值为 null。
想在前置中间件(如鉴权)里用绑定域名怎么办
不建议手动提前触发路由匹配——调用 think\Route::check() 会引发二次路由解析,性能损耗明显,且闭包路由、动态条件等可能导致结果不一致。
更稳的做法是把逻辑后移:
- 改用「路由级中间件」:在
route/app.php中定义路由时加->middleware(\app\middleware\AuthByDomain::class) - 此时
$request->routeInfo()已完整可用,domain键确定存在 - 如果必须全局拦截,可先用
$request->host()做粗筛(如白名单校验),再把细粒度判断交给路由中间件
容易被忽略的关键点
域名绑定不是请求固有属性,而是路由决策的副产品。框架不会在请求对象初始化时就塞进绑定域名,也不会把它当成一个“可随时取用的字段”来维护。你看到的 __domain__ 是路由匹配成功后临时写入 routeInfo 的,生命周期极短,且只在当前请求上下文中有效。一旦中间件顺序错位、路由未命中、或用了非标准路由定义方式(比如闭包里没显式 return),这个值就会消失——这不是 Bug,是设计使然。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











