ThinkPHP5 的 Route::domain() 必须配合闭包或路由数组使用,不支持字符串第二参数;需开启 url_domain_deploy=true,本地调试要修改 hosts 并确保 HTTP_HOST 与配置一致。

ThinkPHP5 的 Route::domain() 必须配合闭包或路由数组使用
直接写 Route::domain('admin.example.com', 'admin/index') 会报错,因为 TP5 不支持字符串作为第二参数。框架只接受闭包函数或返回路由规则的数组。
常见错误现象是:配置写了、域名也解析了,但访问 admin.example.com 仍 404 或跳转到默认模块。
- 正确做法是在
route/route.php(TP5 默认路由文件)中用闭包包裹具体路由:
Route::domain('admin.example.com', function () {
Route::get('/', 'admin/index/index');
Route::get('user/list', 'admin/user/list');
});
- 如果想复用已有路由定义,可导出为数组再传入:
Route::domain('m.example.com', require 'route/mobile_route.php');,但注意mobile_route.php必须return []; - 不建议在中间件或控制器里动态调用
Route::domain(),它只在路由加载阶段生效
必须开启 url_domain_deploy 且不能依赖 APP_SUB_DOMAIN_DEPLOY
TP5.1+ 已废弃 APP_SUB_DOMAIN_DEPLOY,启用域名绑定唯一方式是设置 'url_domain_deploy' => true,位置在 application/config.php。
本地开发时容易忽略这点:即使路由写了 Route::domain(),开关没开,框架就完全不解析 HTTP_HOST。
立即学习“PHP免费学习笔记(深入)”;
- 该配置仅控制“是否启用域名参与路由匹配”,不影响 URL 生成逻辑
- 若同时开了
url_html_suffix,域名绑定后仍会自动追加后缀(如.html),需在闭包内显式关闭:Route::rule('user/:id', 'admin/user/read')->ext(''); - 泛域名写法
Route::domain('*', 'index')要放在精确域名之后,否则会提前拦截所有请求
子域名绑定到控制器时,路径解析以绑定域名为基准
比如 Route::domain('api.example.com', [...]) 下定义 Route::get('v1/user', 'api/v1.User/get'),实际访问地址是 https://api.example.com/v1/user,不是 https://api.example.com/api/v1/user。
容易踩的坑是误以为控制器命名空间会自动补前缀,其实不会——api/v1.User 是完整类名,对应 app\api\controller\v1\User,和模块名无关。
- 若控制器不在默认命名空间下,必须写全路径,例如
'api/v1.User/get'而非'User/get' - URL 生成函数
url('api/v1.User/get')默认不带域名,要输出完整链接得手动拼接或改写__ROOT__ - HTTPS 域名绑定无特殊处理,但回调、重定向等场景需确保
$_SERVER['HTTP_HOST']和配置一致,否则签名或跳转会失败
本地调试务必模拟真实 HTTP_HOST,hosts + 端口匹配很关键
在 localhost:8000 或 127.0.0.1:8080 下测试 admin.example.com,必然失败——因为 $_SERVER['HTTP_HOST'] 是带端口的,而你写的 Route::domain() 没带端口。
最稳妥的本地验证方式是:改 /etc/hosts(macOS/Linux)或 C:\Windows\System32\drivers\etc\hosts(Windows),添加:
127.0.0.1 admin.example.com 127.0.0.1 m.example.com
然后用浏览器直接访问 http://admin.example.com(不带端口),确保 Web 服务器(如 Apache/Nginx)把该 Host 头透传给 PHP。
- 宝塔、XAMPP 等集成环境常默认只响应
localhost,需额外配置虚拟主机或修改站点根目录绑定 - TP5 不会自动剥离端口做模糊匹配,
admin.example.com:80和admin.example.com被视为两个不同域名 - 调试时可用
var_dump($_SERVER['HTTP_HOST']);确认实际值,再决定Route::domain()怎么写



















