Laravel 需用 Route::domain() 显式配置域名路由,仅对内部路由生效;子域名支持通配符如 {sub}.example.com;多域名共享控制器时应在全局中间件解析 Host 并绑定租户上下文;失效主因是 HTTP_HOST 头未正确透传。

怎么让 Laravel 路由匹配不同域名
Laravel 本身不自动根据域名区分路由,必须显式配置域名约束,否则所有域名都会命中同一组路由。关键在 Route::domain() 的使用时机和作用域——它只对内部定义的路由生效,且不能嵌套在 Route::group() 的普通 middleware 或 prefix 分组里混用。
常见错误是把域名逻辑写在中间件里做跳转或重定向,这属于运行时判断,既低效又绕过路由缓存,还容易导致 HTTPS/HTTP 协议不一致问题。
- 每个需要独立域名逻辑的路由组,必须用
Route::domain('xxx.com')包裹 - 子域名支持用通配符:如
Route::domain('{sub}.example.com'),对应路由中需声明$sub参数 - 本地开发时,
127.0.0.1和localhost是两个不同域名,需分别配置或统一 hosts 绑定 - 部署到 Nginx/Apache 时,确保 Web 服务器已将请求的原始
Host头透传给 PHP(Laravel 依赖它做匹配)
Laravel 多域名共享同一套控制器但隔离数据怎么办
典型场景:SaaS 系统中多个客户子域名(如 client1.app.com、client2.app.com)共用 HomeController@index,但要加载各自租户数据。这时不能靠路由参数传递租户 ID,而应在中间件中解析域名并绑定上下文。
推荐做法是在全局中间件(如 App\Http\Middleware\ResolveTenant)里读取 request()->getHost(),查出对应租户,再塞进 Laravel 容器或请求属性中。后续控制器可直接通过 $request->tenant 或 app('tenant') 获取。
- 不要在控制器构造函数里解析域名——路由尚未完全解析完成,
$request->getHost()可能不可靠 - 避免在模型作用域(Scopes)里重复查租户,应提前解析并缓存到请求生命周期内
- 若使用多数据库连接,域名解析后应动态切换
DB::connection(),而不是硬编码连接名
为什么 Route::domain() 不生效?常见环境陷阱
最常踩的坑不是代码写错,而是环境没对齐。Laravel 的域名匹配完全依赖 $_SERVER['HTTP_HOST'],而这个值极易被反向代理、负载均衡或本地 hosts 设置干扰。
- Docker 环境中,Nginx 容器未设置
proxy_set_header Host $host;→$_SERVER['HTTP_HOST']变成上游 IP 或空 - Cloudflare 代理下,默认屏蔽真实 Host 头,需开启「Preserve original host header」或改用
CF-Connecting-IP+ 自定义规则 - Homestead/Valet 下,如果没在
/etc/hosts中为每个测试域名添加映射(如127.0.0.1 site1.test site2.test),浏览器根本不会发对应 Host 头 - Artisan 命令行执行路由列表(
php artisan route:list)不显示 domain 列——它只反映当前 CLI 请求的 Host,不代表实际 Web 请求行为
要不要用 RouteServiceProvider 动态加载域名路由文件
可以,但没必要过度设计。简单项目直接在 routes/web.php 里分块写 Route::domain() 更清晰;复杂 SaaS 项目才值得拆出 routes/domains/ 目录,配合 RouteServiceProvider::loadDomainRoutes() 按需加载。
注意:动态加载本质还是 PHP 文件包含,不会带来性能优势;反而增加调试难度——IDE 可能无法跳转,route:list 输出也不直观。真正影响性能的是路由编译缓存是否启用(php artisan route:cache),而它要求所有路由定义必须是静态可分析的,动态 require 会直接报错。
- 若用了
route:cache,就别在路由文件里写条件判断或函数调用,包括env()、config() - 域名路由一旦缓存,修改后必须重新执行
php artisan route:cache,否则新域名不会生效 - 测试环境建议关掉路由缓存,避免本地改了路由却一直走旧缓存
域名路由的核心其实是 Host 头的可信传递和早期解析——后面所有逻辑都建立在这个基础上。很多人花时间调路由写法,最后发现是 Nginx 没透传头,或者本地 DNS 解析绕过了 hosts。











