laravel子域名路由必须用route::domain()显式声明,只填主机名如'api.example.com',动态段{name}需通过$request->route('name')获取,url()默认不包含子域名,须手动传domain参数。

如何用 Route::domain() 绑定子域名
Laravel 原生支持子域名路由,但必须用 Route::domain() 显式声明,不能靠 Route::group(['domain' => ...]) 或中间件模拟 —— 后者无法触发 Laravel 的域名匹配逻辑。
常见错误是把子域名写成完整 URL(如 'https://api.example.com'),实际只填主机名部分:'api.example.com',协议、路径、端口全不认。
-
Route::domain('admin.example.com')匹配该子域下所有路由,Route::domain('{sub}.example.com')才支持动态捕获 - 本地开发需在 hosts 文件加解析,例如
127.0.0.1 api.example.com admin.example.com - 如果用了 HTTPS,Nginx/Apache 必须把所有子域名都配置为有效虚拟主机,否则 404 不是 Laravel 报的,是 Web 服务器直接拒收
动态子域名参数怎么取值
用 {sub} 这类占位符定义动态域名后,它不会自动进控制器方法参数,必须显式通过 request()->route('sub') 或 $request->route('sub') 获取 —— 别指望像普通路由参数那样直接注入。
示例:Route::domain('{sub}.example.com')->group(...) 中,{sub} 是字符串,不是模型绑定对象;想查用户对应子域名?得自己用 User::where('subdomain', $sub)->first() 查。
- 动态段名必须和路由组内任意一个
Route::get()的参数名一致,否则$request->route('sub')返回 null - 不能在同一个
Route::domain()下混用固定域名和动态段,比如'{sub}.example.com'和'admin.example.com'写在一起,Laravel 会优先匹配字面量,动态段失效 - 若子域名含短横线(如
my-app.example.com),{sub}拿到的是完整字符串,不会被自动转下划线
为什么 url() 和 route() 生成的链接还是主域名
默认情况下,Laravel 生成 URL 时完全忽略当前请求的子域名,只看 APP_URL 配置。即使你正访问 api.example.com,route('user.show', 1) 还是输出 https://example.com/user/1。
解决办法只有两个:手动传 ['domain' => 'api.example.com'] 到 route() 第三个参数,或全局改写 URL 生成器行为(不推荐)。
- 安全起见,别用
request()->getHost()动态拼域名传给route(),攻击者可伪造 Host 头导致 XSS 或跳转漏洞 - 如果项目有多个子域名且路由复用率高,建议封装一个辅助函数,比如
subdomain_route('api.user.show', $id, $sub),内部统一处理域名注入 -
asset()不受影响,它始终走ASSET_URL或APP_URL,跟当前域名无关
中间件里读不到子域名参数?检查路由注册顺序
子域名参数(如 {sub})在中间件执行时已经解析完毕,但前提是:中间件必须注册在 Route::domain() 内部的 group 里。如果把中间件挂到全局中间件堆栈($middleware 数组),或者放在 domain group 外层的 Route::middleware(),$request->route('sub') 就是 null。
本质是 Laravel 路由匹配分两步:先按域名筛选 route collection,再在该 collection 内匹配 path + method。中间件位置决定了它能看到哪一步的结果。
- 正确写法:
Route::domain('{sub}.example.com')->middleware('check.subdomain')->group(...) - 错误写法:
Route::middleware('check.subdomain')->domain('{sub}.example.com')->group(...)—— 此时中间件运行时,路由尚未绑定域名参数 - 如果要用中间件做子域名鉴权(比如禁止未注册子域名访问),必须在中间件里用
$request->getHost()提前解析,而不是等$request->route('sub')
子域名路由真正的复杂点不在写法,而在于它把「域名」这个本该由 DNS 和 Web 服务器处理的概念,强行拉进了应用层路由逻辑。一旦 Nginx 配置漏掉某个子域、或本地 hosts 没同步、或 APP_URL 写死主域名,问题就出在看不见的地方。











