Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Dynamic Subdomain Bundle Laravel Package

netgusto/dynamic-subdomain-bundle

Symfony bundle that brings dynamic subdomain routing to your app: define routes on wildcard hosts like {tenant}.example.com, match incoming subdomains automatically, and generate subdomain-aware URLs with minimal configuration.

View on GitHub
Deep Wiki
Context7

Getting Started

This is a Symfony2-era bundle, not a Laravel package. There is no service provider, no facade, and no config/*.php merge. If you are evaluating it for a Laravel codebase, treat it as a reference implementation of "subdomain → tenant routing" rather than something you will composer require.

Minimal steps in its native Symfony2 context:

  1. composer require netgusto/dynamic-subdomain-bundle (old constraints — expect PHP 5.x/7.0-era platform requirements; you will likely need --ignore-platform-reqs or a legacy PHP container).
  2. Register the bundle in app/AppKernel.php:
    $bundles = [
        // ...
        new Netgusto\DynamicSubdomainBundle\NetgustoDynamicSubdomainBundle(),
    ];
    
  3. Add configuration under app/config/config.yml (the README is the only reliable source of the exact keys — the bundle is small enough to read its DependencyInjection/Configuration.php).
  4. Read Resources/config/services.xml first. That file tells you what the bundle actually wires up — usually a request listener and/or a routing loader — faster than any docs.
  5. First use case: point a wildcard DNS record (*.example.com) at your dev box, define a route whose host contains a placeholder, and confirm the value lands in $request->attributes inside a controller.

The practical "first use case" is a single-tenant-to-multi-tenant pivot: acme.example.com should resolve to the same controller as globex.example.com but with a tenant identifier already parsed out of the host before your controller runs.

Implementation Patterns

Route-level host matching (the pattern actually worth stealing). Symfony2's router has supported host placeholders since 2.2, so most implementations boil down to:

tenant_dashboard:
    path: /dashboard
    host: "{tenant}.example.com"
    defaults: { _controller: AppBundle:Dashboard:index, tenant: null }
    requirements: { tenant: "[a-z0-9-]+" }

The bundle's contribution is typically the listener that normalizes the host into a request attribute before routing, so controllers never touch $request->getHost() directly. In Laravel the direct equivalent is:

Route::domain('{tenant}.example.com')->group(function () {
    Route::get('/dashboard', DashboardController::class);
});

If you are porting, that one-liner in routes/web.php replaces most of what this bundle exists for.

Middleware over listeners. Whatever the bundle does in a Symfony kernel.request listener, the Laravel translation is a middleware that resolves the tenant and either binds it into the container or aborts early:

public function handle($request, Closure $next)
{
    $host = $request->getHost();
    $parts = explode('.', $host);
    $sub = count($parts) > 2 ? $parts[0] : null;

    abort_unless(Tenant::where('slug', $sub)->exists(), 404);
    app()->instance(Tenant::class, Tenant::where('slug', $sub)->firstOrFail());

    return $next($request);
}

Register it globally after SubstituteBindings so route-model binding sees the tenant, and scope it to tenant routes only to avoid running tenant lookups on your marketing site or health checks.

Environment-sensitive host parsing. The classic integration bug: localhost has no subdomain, acme.localhost has one, and acme.example.test:8000 has a port. Parse defensively (parse_url on a synthesized scheme, or Str::before($host, '.') guarded by a dot count), and keep a config flag for the base domain so a future domain migration does not mean grepping for string literals.

Pair with a tenant-aware connection resolver. In the Symfony world this bundle is often the front half of a multi-database setup. In Laravel, put the tenant switching in the same middleware (config(['database.connections.tenant.database' => $tenant->db]) then DB::purge('tenant')), so resolution and connection selection are one atomic step. Splitting them across two middlewares is how you end up querying the wrong database under Octane.

Gotchas and Tips

It is effectively unmaintained. Symfony2 only, 14 stars, zero dependents, last activity from the Symfony 2.x era. Do not build a new product on it. Its value today is as a 200-line reference for how host-placeholder routing was done before frameworks made it a first-class feature. Laravel has had Route::domain() for years; use that.

Wildcard DNS is a prerequisite, not a detail. The bundle cannot conjure subdomains. You need *.example.com A <ip> (or a wildcard on your load balancer) plus matching TLS. Symmetric wildcard certs (*.example.com) cover one label only — a.b.example.com will fail TLS. Test with dig before debugging PHP.

Host vs. subdomain confusion. $request->getHost() excludes the port but includes every label. Behind a proxy or Heroku-style router, the Host header may be rewritten — set trusted proxies (Request::setTrustedProxies) or your subdomain parsing silently sees the internal hostname. This is the number one reason "it works locally but not in staging."

Route caching will bite you. Symfony compiled routes and Laravel's route:cache both freeze host patterns. Clear caches after editing wildcard rules; a stale cache produces a 404 that looks like a DNS problem.

Subdomain enumeration is an attack surface. If an unknown subdomain falls through to a 404 that says "tenant not found," you have a tenant-enumeration oracle. Return a generic 404, and rate-limit tenant resolution lookups — this bundle predates that concern entirely.

Extension point. The bundle's real hook is its request listener; if you must keep it alive, decorate that service rather than forking. Look for a service ID like netgusto.dynamic_subdomain.listener and override it in services.yml with a decorates: key.

Debugging shortcut. When routing "mysteriously" fails on a subdomain, dump $request->getHost() and $request->attributes->all() at the top of kernel.request (priority > 32 so it runs before the router). Ninety percent of issues are a proxy-rewritten Host header or a requirement regex rejecting uppercase labels ([a-z0-9-]+ will not match ACME — normalize with strtolower first).

Migration advice for Laravel teams: skip the bundle. You need three pieces — Route::domain(), a tenant-resolution middleware, and a per-tenant DB/cache prefix — and all three are first-party. Budget half a day, not a legacy-container spelunking expedition.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator
equipc/laravel-email-database-log
adedsec/nova-translated-labels