Search by

microweber-packages / microweber-multisite

boksiora

Per-domain multisite isolation for Laravel/Microweber: resolves a site from the Host header (config/<domain>/microweber.php marker) and gives each site its own database, cache, session and media/userfiles tree on one shared codebase.

Package info

github.com/microweber-packages/microweber-multisite

Homepage

pkg:composer/microweber-packages/microweber-multisite

Statistics

Installs: 1

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.0.0 2026-11-03 14:28 UTC

This package is auto-updated.

Last update: 2026-11-03 17:07:16 UTC


README

Per-domain multisite isolation for Laravel — and Microweber on top of it. One checkout of the codebase serves N independent sites, each with its own database, cache, session and media/userfiles tree, selected by the incoming Host: header.

It is a standalone Laravel package (Spatie package-tools) and can be used in a clean Laravel app as a proof of concept. It depends on microweber-packages/microweber-env-writer to write each domain's .env.<domain> credentials.

How a site is resolved

A site is a domain that has a marker file:

config/
├── domain-a.com/microweber.php   ← marker (presence is the signal)
├── domain-b.com/microweber.php
└── app.php                       ← normal shared config

On each request MultisiteManager::detectSiteFromHost() takes the host, strips www. and :port, lowercases it, rejects path traversal, and returns the domain iff config/<domain>/microweber.php exists. No match → the app runs as the default single site (zero behaviour change).

This is the same contract Laravel's environment detector already uses, so the domain also becomes the Laravel environment and loads .env.<domain> (hence the per-site database).

What gets isolated

Subsystem Default site domain-a.com
Database .env .env.domain-a.com (own DB_DATABASE)
Cache prefix config('cache.prefix') mw_domain_a_com_cache
Cache files storage/framework/cache storage/framework/cache/sites/domain-a.com
Session cookie laravel_session mw_domain_a_com_session
Session files storage/framework/sessions storage/framework/sessions/sites/domain-a.com
Userfiles / media storage/app/public/userfiles storage/app/public/userfiles/domain-a.com

Each subsystem is toggleable in config/multisite.php (isolate.*).

Two integration seams

  1. Autoload-time constant (src/helpers.php, registered via composer autoload.files). Runs before any service provider and before Microweber's bootstrap.php, defining MW_USERFILES_FOLDER_NAME = "userfiles/<site>" so every userfiles_path() / media_base_path() call is already per-site. In a clean Laravel app this define is simply inert.

  2. Service provider (MultisiteServiceProvider) applies the cache/session (and optional public-disk) config overrides during register(), before those managers are first resolved.

Clean-Laravel PoC

composer require microweber-packages/microweber-multisite

Enable media isolation without Microweber by turning on the public disk in config/multisite.php:

'isolate' => ['cache' => true, 'session' => true, 'userfiles' => false, 'public_disk' => true],

Create config/<domain>/microweber.php (any file returning []), point the domain at the app, and the Storage::disk('public'), cache and session for that host are transparently namespaced.

Commands

php artisan multisite:list                 # show configured sites + their isolated paths
php artisan multisite:install domain-a.com \
    --db-name=mw_domain_a --db-username=root --db-password=secret

multisite:install creates the config/<domain>/microweber.php marker and writes .env.<domain> (via env-writer) with the DB credentials, cache prefix and session cookie. Then create the DB and point DNS at the server.

Per-domain env folder (optional)

By default the .env.<domain> files sit in the application root next to .env. To collect them in a folder instead, set env_path (config multisite.env_path):

# a sibling "envs/" folder (relative to the app base), or an absolute path
export MULTISITE_ENV_PATH=envs
php artisan multisite:install domain-a.com --db-name=mw_domain_a ...
# → writes envs/.env.domain-a.com; booted from there automatically
  • The value must be a real OS environment variable (web-server SetEnv, docker environment:, or a shell export) — not a line in .env. It decides which env file to load, so it has to be known before any .env is read, exactly like APP_ENV.
  • Relative values resolve against the app base; absolute values are used as-is. Keep the folder outside the public/ docroot (the app root and a sibling envs/ both are).
  • A relocated .env.<domain> is a partial overlay: the shared base .env still fills the keys it omits (APP_KEY, MAIL_*, …) while the per-site values win.
  • Leave it unset (the default) to keep every env file in the root — unchanged behaviour.

API

use MicroweberPackages\Multisite\Facades\Multisite;

Multisite::current();            // 'domain-a.com' | null (default site)
Multisite::isActive();           // bool
Multisite::sites();              // ['domain-a.com', 'domain-b.com']
Multisite::userfilesFolder();    // 'userfiles/domain-a.com'
Multisite::cachePrefix();        // 'mw_domain_a_com_cache'
Multisite::envDir();             // '/app/envs' | null (root layout)
Multisite::siteEnvFile('domain-a.com'); // absolute path to its .env.<domain>

Tests

cd packages/microweber-multisite && composer install && composer test
# or from the project root:
php vendor/bin/phpunit packages/microweber-multisite/tests