必须开启 url_domain_deploy 并将 Route::domain('*') 写在 application/route.php,同时配置 Nginx 通配符 server_name、hosts 文件及 url_domain_root,否则泛域名路由不生效。

ThinkPHP 5 的泛域名绑定必须显式开启 url_domain_deploy,且 Route::domain('*') 规则要写在 route.php(非 route/app.php),否则请求根本不会进入子域名匹配逻辑。
必须开启 url_domain_deploy 才能触发域名路由
TP5 不像 TP6 那样默认识别 Host,它靠开关驱动整个域名路由机制。不打开这个配置,Route::domain() 写得再全也完全无效。
- 在
application/config.php中确保存在并设为true:'url_domain_deploy' => true - 该配置必须放在顶层数组,不能嵌套在
app或route子键下 - 若使用多模块结构(如
application/home/、application/admin/),仍只需在主config.php开关,无需各模块重复配 - 旧版 TP5.0 还有
APP_SUB_DOMAIN_DEPLOY,但那是 TP3 遗留字段,TP5.1+ 已废弃,设了也没用
Route::domain('*') 必须写在 application/route.php
TP5 的域名路由注册入口固定为 application/route.php,不是 route/app.php(那是 TP6 的位置),也不是中间件或控制器里。
- 错误写法:
Route::domain('*', 'home')—— 第二个参数为字符串会报Invalid route rule - 正确写法是闭包或数组:
Route::domain('*', function () { Route::get('index', 'index/index'); }); - 泛规则必须放在精确规则之后,比如先写
Route::domain('admin.example.com', ...),再写Route::domain('*', ...),否则*会提前截断所有匹配 - 本地开发时,
HTTP_HOST可能是localhost:8080,此时Route::domain('*')仍生效,但request()->subDomain()返回空 —— 因为没有点分隔,需手动从request()->host()解析
Nginx 和 hosts 必须配合通配符,否则请求进不了 PHP 层
框架层的 Route::domain('*') 再正确,如果 Web 服务器没把 xxx.example.com 的请求转发给 TP 入口,一切白搭。
立即学习“PHP免费学习笔记(深入)”;
- Nginx 配置中
server_name必须含通配符,例如:server_name *.example.com example.com;;写成server_name *.example.com;会漏掉主域名访问 - 不要用
server_name ~^(?.+)\.example\.com$;这类正则——TP5 不依赖 Nginx 正则传参,只认原始HTTP_HOST值 - 本地测试务必改
/etc/hosts(macOS/Linux)或C:\Windows\System32\drivers\etc\hosts(Windows),加多行:127.0.0.1 a.example.com、127.0.0.1 b.example.com,不能只写一行*.example.com - 浏览器访问时必须输完整域名(如
http://a.example.com),不能用http://localhost加 Referer 模拟,因为 TP5 只读$_SERVER['HTTP_HOST'],不查 header 其他字段
子域名提取要手动做,request()->subDomain() 在泛解析下不可靠
TP5 的 subDomain() 方法默认按 url_domain_root 切分,但泛解析场景下常因根域名配置缺失或不匹配返回空或错值。
- 先确认是否设置了
url_domain_root:如主域是example.com.cn,就得在config.php加'url_domain_root'=>'example.com.cn',否则subDomain()会把a.example.com.cn解成a.example - 更稳妥的方式是绕过框架方法,直接处理:
$host = request()->host(); $sub = strtok($host, '.');(取第一段),再过滤非法字符 -
subDomain()对www.example.com返回空字符串,不是null,别用=== null判断,要用empty()或== '' - 提取后建议立刻存入 Request 属性:
request()->withAttr('subdomain', $sub),后续控制器统一用request()->attr('subdomain'),避免重复解析
泛域名看着只是一行 Route::domain('*'),实际要对齐 Nginx 配置、hosts 文件、TP5 的 url_domain_root、subDomain() 行为和路由注册位置这五处,缺一不可。最容易卡住的是 url_domain_deploy 没开,或者把路由写到了错误的文件里 —— 这两个点不解决,后面所有调试都是徒劳。



















