Q&A
How to create and manage subdomains in Laravel applications
Overview
Subdomain routing lets one Laravel app serve different content or tenants based on the subdomain in the URL. Laravel matches the request's host against domain patterns you define, can extract a dynamic part as a parameter, and routes accordingly. It's the backbone of multi-tenant SaaS where each customer gets their-name.yourapp.com.
graph TD
A["Request: tenant1.example.com"] --> B["Route::domain('{account}.example.com')"]
B --> C[Capture account = 'tenant1']
C --> D[Pass to controller / middleware]
D --> E[Serve tenant-specific data]
style B fill:#1e1b4b,color:#fff
Static Subdomain
Route a fixed subdomain to specific routes:
use Illuminate\Support\Facades\Route;
Route::domain('admin.example.com')->group(function () {
Route::get('/', [AdminController::class, 'dashboard']);
Route::get('/users', [AdminController::class, 'users']);
});
Requests to admin.example.com hit these routes; others don't.
Dynamic Subdomain (multi-tenant)
Capture the subdomain as a parameter with {}:
Route::domain('{account}.example.com')->group(function () {
Route::get('/', function (string $account) {
return "Welcome to the {$account} workspace";
});
Route::get('/dashboard', [DashboardController::class, 'show']);
});
The {account} value is injected as the first argument to route closures/controllers (before other route parameters):
public function show(string $account)
{
$tenant = Tenant::where('slug', $account)->firstOrFail();
// load tenant-scoped data
}
Resolving the Tenant via Middleware
For multi-tenancy, centralize tenant resolution in middleware so every route is scoped consistently:
class IdentifyTenant
{
public function handle($request, Closure $next)
{
$account = $request->route('account');
$tenant = Tenant::where('slug', $account)->firstOrFail();
app()->instance('currentTenant', $tenant); // make it available app-wide
return $next($request);
}
}
Route::domain('{account}.example.com')
->middleware(IdentifyTenant::class)
->group(function () { /* tenant routes */ });
DNS and Local Setup
Routing only works if requests actually reach your app under those hosts:
- Production: configure a wildcard DNS record (
*.example.com) pointing to your server, and a wildcard/served virtual host (and TLS cert, e.g. a wildcard certificate) for HTTPS. - Local dev: map subdomains in your hosts file or use a tool. With Laravel Valet,
*.testwildcard domains work automatically; with Sail/Homestead, configure accordingly.
Generating URLs to Subdomains
When defining named routes inside a domain group, route URL generation includes the domain parameter:
Route::domain('{account}.example.com')->group(function () {
Route::get('/billing', [BillingController::class, 'index'])->name('billing');
});
// route('billing', ['account' => 'tenant1']) → http://tenant1.example.com/billing
Packages for Serious Multi-Tenancy
For full SaaS multi-tenancy (separate databases per tenant, tenant-aware queries, etc.), dedicated packages like stancl/tenancy or spatie/laravel-multitenancy build on this routing and handle database switching, caching, and isolation. Mention them when the scope is real multi-tenancy rather than a couple of subdomains.
Why This Matters in Interviews
Laravel interviews use this to test routing depth and multi-tenancy awareness. Key points: Route::domain(...) with a {param} captures the subdomain, which is passed as the first controller/closure argument; middleware is the clean place to resolve and scope the tenant; and routing requires matching wildcard DNS (and TLS) to actually deliver requests. Knowing that real multi-tenancy uses dedicated packages for DB isolation shows practical scope awareness.
Common Pitfalls
- Forgetting the subdomain parameter is the first argument, mis-ordering controller params.
- Routing configured but DNS/wildcard not set up, so subdomains never reach the app.
- Missing a wildcard TLS certificate, breaking HTTPS on subdomains.
- Not scoping queries to the tenant, leaking data across tenants (a serious security bug).
- Reinventing full multi-tenant isolation instead of using a maintained package.
References
- Laravel Routing - subdomain routing: https://laravel.com/docs/routing#route-group-subdomain-routing
- stancl/tenancy package: https://tenancyforlaravel.com/