kunstmaan/multidomain-bundle
A Symfony bundle that adds multi-domain support to Kunstmaan CMS, letting a single installation serve several domains with their own hosts, locales, and content. Configure domains, route requests to the right site, and manage per-domain settings without duplicating your application.
Read this first: it is a Symfony bundle, not a Laravel package. kunstmaan/multidomain-bundle is an add-on for KunstmaanCMS (kunstmaan/bundles-cms), built on Symfony's HTTP kernel, Doctrine, and the Kunstmaan node/page tree. There is no service provider, no config/multidomain.php, and no way to composer require it into a Laravel app. What you can port is the pattern — host header to site/locale resolution — which in Laravel is a config array plus ~40 lines of middleware.
If you are actually working in a Kunstmaan/Symfony codebase, the minimum path is:
composer require kunstmaan/multidomain-bundle alongside a compatible kunstmaan/bundles-cms version (check the bundle's composer.json constraints first — this is an old, low-traffic package and version alignment is the most common first-day failure).app/AppKernel.php (or config/bundles.php on Symfony 4+ Flex-style setups), after the Kunstmaan CMS bundles.DependencyInjection/Configuration.php inside the bundle, not the README. Typically it looks like:kunstmaan_multi_domain:
hosts:
example.com:
default_locale: en
locales: [en, de]
example.fr:
default_locale: fr
locales: [fr]
curl -H "Host: example.fr" http://localhost:8000/ and confirm the locale and content change.First use case to validate: two public hostnames serving the same node tree, each with a different default locale, plus a separate admin host. If your default_locale swap works and your page URLs stop leaking the wrong host, the bundle is doing its job.
Where to look first, in order: DependencyInjection/Configuration.php (what config actually exists), the request/kernel.request listener (how host is resolved and what happens on a miss), and the domain/configuration service (the extension point you will decorate).
Config as the single source of truth for the host map. Put every hostname — production, staging, and local — in config, and derive everything else (canonical URLs, hreflang, sitemaps, robots) from that map. Hardcoding a second list of domains in a Twig template or a controller is how multi-domain sites drift.
Keep the admin/backend host separate from frontend hosts. If the CMS backend answers on the same host as a site, host-based resolution will fight your security config and your cache. Give it its own hostname and exclude it from the frontend listener where possible.
Always generate absolute URLs from the resolved domain, not from the current Request. In CLI, cron, and queue contexts there is no request host, so absolute_url() falls back to router.request_context. Set the host explicitly before generating URLs:
$context = $this->router->getContext();
$context->setHost($domain->getHost());
$context->setScheme('https');
Do this in sitemap generators, mail builders, and any command that emits links. Otherwise your nightly sitemap is full of http://localhost.
Locale negotiation as a fallback chain: host default locale → explicit user choice (session/query) → Accept-Language → global default. The bundle handles the first hop; the rest is your listener with a higher priority.
Per-domain SEO surfaces. Generate one sitemap per host, one robots.txt per host (via a controller keyed off the resolved domain), and emit hreflang alternates by iterating the same host map. This is the payoff of centralising the config.
Cache keys must include the host. Any full-page cache, Varnish rule, Cloudflare cache rule, or Symfony HttpCache setup needs Host in the key. A page cached from example.fr served to example.com is the classic multi-domain outage.
Test with explicit hosts. In functional tests, request the full URL ($client->request('GET', 'https://example.fr/page')) rather than a path, and assert on locale/content per host. If you use X-Forwarded-Host in tests, configure trusted proxies or the listener won't see it.
Host is attacker-controlled. The bundle trusts the header by design. Only honour X-Forwarded-Host behind framework.trusted_proxies/trusted_hosts, and decide deliberately what an unknown host does — a 404, a redirect to a canonical host, or a silent fallback to the first domain. Silent fallback is a duplicate-content and SEO trap.localhost:8000 and example.com. are not localhost and example.com. Normalise in your resolution layer or dev will work and production will not (or vice versa).default_locale must be inside locales. A mismatch produces either a redirect loop or a 404 on the homepage, depending on how your locale listener is ordered. Assert this at container build time if you can.kernel.request priority matters: if Symfony's LocaleListener runs first, the host-derived locale is overwritten. Check priorities with debug:event-dispatcher kernel.request when the wrong locale "randomly" appears.bundles-cms upgrade.sites config keyed by host, a ResolveSite middleware that runs before SetLocale in the web group, URL::defaults() or an overridden UrlGenerator for absolute links, and Cache::tags()/host-prefixed keys. That gives you the same behaviour in a fraction of the surface area, with no Symfony/Doctrine coupling.How can I help you explore Laravel packages today?