Laravel子域名路由必须显式用Route::domain()声明且置于routes/web.php顶部,否则因顺序匹配优先原则被根域名路由拦截;需配置DNS、Web服务器泛解析、动态APP_URL、session.domain=.example.com及/etc/hosts本地映射。

子域名路由在 Laravel 中不是“开箱即用”的功能,它必须显式用 Route::domain() 声明,否则请求会直接落入根域名路由或 404 —— 这是绝大多数人踩坑的第一步。
Route::domain() 必须写在 routes/web.php 最前面
Laravel 路由匹配是顺序优先(First Match Wins),如果把 Route::domain('admin.example.com') 放在 Route::get('/', ...) 后面,访问 admin.example.com/ 时,根域名的 / 路由会先命中并拦截,子域名分组根本不会执行。
- ✅ 正确做法:所有
Route::domain()分组必须置于routes/web.php文件顶部,早于任何根域名路由 - ⚠️ 注意:
Route::domain('{sub}.example.com')和Route::domain('admin.example.com')可共存,但通配符路由应放在更具体的之后(如先写死api.example.com,再写通配{sub}.example.com),避免误匹配 - ? 本地开发时,
/etc/hosts必须添加对应行,例如:127.0.0.1 admin.example.com api.example.com;否则浏览器发不出带正确Host头的请求
动态子域名参数 {sub} 必须被显式接收
{sub} 是路由参数,不是自动注入的全局变量。若你在 Route::domain('{sub}.example.com') 中用了它,就必须在闭包函数签名或控制器方法参数里声明,否则拿不到值,甚至报错 Missing argument。
- ✅ 正确写法(闭包):
Route::domain('{sub}.example.com')->group(function () { Route::get('/', function ($sub) { return "Welcome to {$sub}"; }); }); - ✅ 正确写法(控制器):
public function index($sub) { ... },且路由需绑定为Route::get('/', [SubdomainController::class, 'index']) - ⚠️ 错误写法:
function () { $sub = request()->route('sub'); }—— 看似能取到,但request()->route()在中间件中可能为 null,不稳定 - ? 安全建议:对通配符加约束,比如
->where('sub', 'shop|admin|api'),防止hacker.example.com也被匹配
APP_URL 和 URL 生成必须动态化
硬编码 APP_URL=https://example.com 会导致 route()、url()、邮件链接全部指向错误域名 —— Laravel 不会自动感知当前请求的 Host。
- ✅ 推荐做法:在
config/app.php中把'url' => env('APP_URL', request()->getSchemeAndHttpHost()),让框架运行时读取当前请求协议+主机名 - ⚠️ 如果用了 Nginx 反向代理或 CDN,确保
App\Http\Middleware\TrustProxies已启用,并配置了可信 IP 段,否则request()->getHost()可能返回内网地址(如10.0.1.5) - ? 不要用字符串拼接生成跳转链接,比如
redirect('https://admin.example.com/dashboard')—— 这样会丢失 HTTPS 切换、端口、路径前缀等上下文
Session 和 Cookie 跨子域名必须设 session.domain
浏览器同源策略默认禁止 admin.example.com 和 www.example.com 共享 Cookie,导致登录态丢失、Session 不同步 —— 这和 Laravel 无关,是 HTTP 规范。
- ✅ 解决方案:在
config/session.php中设置'domain' => '.example.com'(注意开头的点) - ⚠️ 必须配合 HTTPS 使用泛域名证书(
*.example.com),否则现代浏览器会拒绝设置该 Cookie - ? 验证方式:打开 DevTools → Application → Cookies,检查 Cookie 的 Domain 字段是否为
.example.com - ? 补充:若只在部分子域间共享(如仅
shop和checkout),可单独设中间件,在请求进入前动态调用session()->setDomain('.shop.example.com')
最常被忽略的是 Web 服务器配置 —— Nginx/Apache 必须把所有子域名统一指向 Laravel 的 public/ 目录,而不是靠 Laravel 自己“监听”域名。哪怕路由写得再准,请求没进 index.php,一切都没意义。











