Try Live
Add Docs
Rankings
Pricing
Docs
Install
Install
Docs
Pricing
More...
More...
Try Live
Rankings
Enterprise
Create API Key
Add Docs
Laravel Multidomain
https://github.com/gecche/laravel-multidomain
Admin
Laravel Multidomain is a package that enables a single Laravel installation to serve multiple HTTP
...
Tokens:
7,054
Snippets:
78
Trust Score:
8.5
Update:
1 week ago
Context
Skills
Chat
Benchmark
81.4
Suggestions
Latest
Show doc for...
Code
Info
Show Results
Context Summary (auto-generated)
Raw
Copy
Link
# Laravel Multidomain Laravel Multidomain is a package that enables a single Laravel installation to serve multiple HTTP domains with domain-specific environment files, storage paths, and database configurations. This multi-tenancy solution allows different customers to share the same application codebase while maintaining completely separate configurations, storage folders, and data isolation. The package works by detecting the current HTTP domain and loading the appropriate `.env.{domain}` file and storage directory automatically. It extends Laravel's core Application class and provides artisan commands for managing domains. Both HTTP requests and CLI commands support domain-specific configurations through automatic detection or the `--domain` option. ## Installation ### Add Package to Composer Install the package using Composer to add multi-domain support to your Laravel application. ```bash composer require gecche/laravel-multidomain:13.* ``` ### Replace Laravel Application Class Modify the `bootstrap/app.php` file to use the extended Application class that provides domain detection and management capabilities. ```php <?php // Replace the default Laravel Application with Multidomain version // use Illuminate\Foundation\Application; use Gecche\Multidomain\Foundation\Application; use Illuminate\Foundation\Configuration\Exceptions; use Illuminate\Foundation\Configuration\Middleware; $environmentPath = null; // Use default, or set custom path for .env files $domainParams = []; return Application::configure( basePath: dirname(__DIR__), environmentPath: $environmentPath, domainParams: $domainParams ) ->withRouting( web: __DIR__.'/../routes/web.php', commands: __DIR__.'/../routes/console.php', health: '/up', ) ->withMiddleware(function (Middleware $middleware) { // }) ->withExceptions(function (Exceptions $exceptions) { // })->create(); ``` ### Configure QueueServiceProvider Override the QueueServiceProvider in `config/app.php` to enable domain-aware queue processing. ```php <?php // config/app.php return [ 'providers' => \Illuminate\Support\ServiceProvider::defaultProviders()->merge([ // Package Service Providers... ])->replace([ // Replace Laravel's QueueServiceProvider with Multidomain version \Illuminate\Queue\QueueServiceProvider::class => \Gecche\Multidomain\Queue\QueueServiceProvider::class, ])->merge([ // Added Service Providers... ])->toArray(), // ... rest of config ]; ``` ## Artisan Commands ### domain:add - Add a New Domain The `domain:add` command creates a new domain configuration including environment file and storage directories for tenant isolation. ```bash # Add a new domain with default configuration php artisan domain:add site1.com # Add domain with custom environment values php artisan domain:add site2.com --domain_values='{"DB_DATABASE":"site2_db","CACHE_PREFIX":"site2"}' # Add domain with force flag to recreate storage directories php artisan domain:add site1.com --force # Add domain for local development (updates .gitignore) php artisan domain:add dev.local --dev # Expected output: # Added site1.com to the application. # Creates: # - .env.site1.com (environment file) # - storage/site1_com/ (storage directory with app/, framework/, logs/ subdirectories) ``` ### domain:remove - Remove an Existing Domain The `domain:remove` command removes a domain's environment file and optionally its storage directory from the application. ```bash # Remove domain (keeps storage directory) php artisan domain:remove site2.com # Remove domain and delete storage directory php artisan domain:remove site2.com --force # Expected output: # Removed site2.com from the application. # Deletes: # - .env.site2.com (always deleted) # - storage/site2_com/ (only with --force flag) ``` ### domain:list - List Installed Domains The `domain:list` command displays all configured domains with their associated environment files and storage paths. ```bash # List domains as formatted text (default) php artisan domain:list # Expected output: # Domain: site1.com # - Storage dir: /var/www/app/storage/site1_com # - Env file: /var/www/app/.env.site1.com # # Domain: site2.com # - Storage dir: /var/www/app/storage/site2_com # - Env file: /var/www/app/.env.site2.com # List domains as JSON php artisan domain:list --output=json # Expected output: # [{"domain":"site1.com","storage_dir":"/var/www/app/storage/site1_com","env_file":"/var/www/app/.env.site1.com"}] # List domains as table php artisan domain:list --output=table # Expected output: # +------------+----------------------------------+---------------------------+ # | domain | storage_dir | env_file | # +------------+----------------------------------+---------------------------+ # | site1.com | /var/www/app/storage/site1_com | /var/www/app/.env.site1.com | # +------------+----------------------------------+---------------------------+ ``` ### domain:update_env - Update Environment Variables The `domain:update_env` command updates environment variables across one or all domain configuration files. ```bash # Update all domain .env files with new values php artisan domain:update_env --domain_values='{"MAIL_DRIVER":"smtp","MAIL_HOST":"mail.example.com"}' # Update specific domain's .env file only php artisan domain:update_env site1.com --domain_values='{"DB_DATABASE":"site1_production"}' # Expected output: # Updated env domain files # Example: Adding queue configuration to all domains php artisan domain:update_env --domain_values='{"QUEUE_CONNECTION":"redis","REDIS_HOST":"127.0.0.1"}' ``` ### Using --domain Option with Any Artisan Command All Laravel artisan commands accept the `--domain` option to execute in the context of a specific domain. ```bash # Run migrations for a specific domain php artisan migrate --domain=site1.com # Clear cache for a specific domain php artisan cache:clear --domain=site2.com # Run tinker in domain context php artisan tinker --domain=site1.com # Generate config cache for specific domain php artisan config:cache --domain=site2.com # Creates: bootstrap/cache/config-site2_com.php # Run any command with domain context php artisan queue:work --domain=site1.com --queue=default1 php artisan schedule:run --domain=site1.com ``` ## Application API Methods ### app()->domain() - Get Current Domain The `domain()` method returns the current HTTP domain name or checks if the application is running under specific domains. ```php <?php // Get the current domain name $currentDomain = app()->domain(); // Returns: "site1.com" // Check if running under a specific domain if (app()->domain('site1.com')) { // Domain is site1.com } // Check if running under one of multiple domains if (app()->domain('site1.com', 'site2.com')) { // Domain is either site1.com or site2.com } // Use in controllers class TenantController extends Controller { public function dashboard() { $domain = app()->domain(); $tenantConfig = config("tenants.{$domain}"); return view('dashboard', [ 'domain' => $domain, 'config' => $tenantConfig, ]); } } ``` ### app()->fullDomain() - Get Full Domain with Scheme The `fullDomain()` method returns the complete domain including HTTP scheme and port. ```php <?php // Get full domain with scheme and port $fullDomain = app()->fullDomain(); // Returns: "https://site1.com:8443" or "http://site1.com" // Check specific full domain if (app()->fullDomain('https://secure.site1.com')) { // Handle secure subdomain } // Access individual components $scheme = app('domain_scheme'); // "https" or "http" $domain = app('domain'); // "site1.com" $port = app('domain_port'); // 443, 80, or custom port ``` ### app()->domainsList() - Get All Configured Domains The `domainsList()` method returns an associative array containing all installed domains with their storage paths and environment files. ```php <?php // Get list of all configured domains $domains = app()->domainsList(); // Returns: // [ // 'site1.com' => [ // 'storage_path' => '/var/www/app/storage/site1_com', // 'env' => '.env.site1.com' // ], // 'site2.com' => [ // 'storage_path' => '/var/www/app/storage/site2_com', // 'env' => '.env.site2.com' // ] // ] // Iterate over domains for admin dashboard foreach (app()->domainsList() as $domain => $info) { echo "Domain: {$domain}\n"; echo "Storage: {$info['storage_path']}\n"; echo "Env File: {$info['env']}\n"; } ``` ### app()->domainStoragePath() - Get Domain Storage Path The `domainStoragePath()` method returns the storage directory path for the current or specified domain. ```php <?php // Get storage path for current domain $storagePath = app()->domainStoragePath(); // Returns: "/var/www/app/storage/site1_com" // Get storage path for specific domain $storagePath = app()->domainStoragePath('site2.com'); // Returns: "/var/www/app/storage/site2_com" // Use storage_path() helper - automatically domain-aware $logPath = storage_path('logs/laravel.log'); // Returns: "/var/www/app/storage/site1_com/logs/laravel.log" // Store domain-specific files $uploadPath = storage_path('app/uploads'); file_put_contents("{$uploadPath}/document.pdf", $content); ``` ## Helper Functions ### domain_sanitized() - Sanitize Domain Name The `domain_sanitized()` function converts dots in domain names to underscores for filesystem-safe naming. ```php <?php // Convert domain to filesystem-safe name $sanitized = domain_sanitized('site1.com'); // Returns: "site1_com" // Use custom replacement character $sanitized = domain_sanitized('sub.site1.com', '-'); // Returns: "sub-site1-com" // Practical usage for file naming $cacheFile = 'config-' . domain_sanitized(app()->domain()) . '.php'; // Returns: "config-site1_com.php" ``` ### domain_root_url() - Get Domain Root URL The `domain_root_url()` function returns the root URL of the current domain with optional path appended. ```php <?php // Get root URL of current domain $rootUrl = domain_root_url(); // Returns: "https://site1.com" or "http://site1.com:8080" // Get URL with path appended $apiUrl = domain_root_url('/api/v1/users'); // Returns: "https://site1.com/api/v1/users" // Use in email templates or notifications $resetLink = domain_root_url('/password/reset/' . $token); // Returns: "https://site1.com/password/reset/abc123" ``` ### env_path() - Get Environment Files Directory The `env_path()` function returns the path to the directory containing environment files. ```php <?php // Get environment files directory $envDir = env_path(); // Returns: "/var/www/app" (or custom path if configured) // Get path to specific env file $envFile = env_path('.env.site1.com'); // Returns: "/var/www/app/.env.site1.com" // Check if domain env file exists if (file_exists(env_path('.env.' . $domain))) { // Domain-specific config exists } ``` ## Custom Domain Detection ### Configure Custom Detection Function Customize how the package detects the current HTTP domain by providing a closure in the Application configuration. ```php <?php // bootstrap/app.php use Gecche\Multidomain\Foundation\Application; use Illuminate\Foundation\Configuration\Exceptions; use Illuminate\Foundation\Configuration\Middleware; $environmentPath = null; // Custom domain detection using HTTP_HOST instead of SERVER_NAME $domainParams = [ 'domain_detection_function_web' => function() { return \Illuminate\Support\Arr::get($_SERVER, 'HTTP_HOST'); } ]; return Application::configure( basePath: dirname(__DIR__), environmentPath: $environmentPath, domainParams: $domainParams ) ->withRouting( web: __DIR__.'/../routes/web.php', commands: __DIR__.'/../routes/console.php', health: '/up', ) ->withMiddleware(function (Middleware $middleware) { // }) ->withExceptions(function (Exceptions $exceptions) { // })->create(); ``` ### Advanced Detection with Request Headers Implement domain detection based on custom headers or request attributes for proxy setups. ```php <?php // bootstrap/app.php $domainParams = [ 'domain_detection_function_web' => function() { // Check X-Forwarded-Host header first (for reverse proxy) $forwardedHost = \Illuminate\Support\Arr::get($_SERVER, 'HTTP_X_FORWARDED_HOST'); if ($forwardedHost) { // Take first host if multiple (comma-separated) return explode(',', $forwardedHost)[0]; } // Fall back to HTTP_HOST $httpHost = \Illuminate\Support\Arr::get($_SERVER, 'HTTP_HOST'); if ($httpHost) { // Remove port if present return explode(':', $httpHost)[0]; } // Final fallback to SERVER_NAME return \Illuminate\Support\Arr::get($_SERVER, 'SERVER_NAME', 'localhost'); } ]; ``` ## Queue Configuration ### Configure Domain-Specific Queues Set up separate queues for each domain to process jobs independently using different queue names. ```php <?php // .env.site1.com // QUEUE_CONNECTION=database // QUEUE_DEFAULT=site1_queue // DB_DATABASE=site1_db // .env.site2.com // QUEUE_CONNECTION=database // QUEUE_DEFAULT=site2_queue // DB_DATABASE=site2_db // config/queue.php return [ 'default' => env('QUEUE_CONNECTION', 'sync'), 'connections' => [ 'database' => [ 'driver' => 'database', 'table' => 'jobs', 'queue' => env('QUEUE_DEFAULT', 'default'), 'retry_after' => 90, ], 'redis' => [ 'driver' => 'redis', 'connection' => 'default', 'queue' => env('QUEUE_DEFAULT', 'default'), 'retry_after' => 90, ], ], ]; ``` ### Run Domain-Specific Queue Workers Start queue workers for specific domains using the `--domain` option. ```bash # Start worker for site1.com php artisan queue:work --domain=site1.com --queue=site1_queue # Start worker for site2.com php artisan queue:work --domain=site2.com --queue=site2_queue # Run queue listener for specific domain php artisan queue:listen --domain=site1.com --queue=site1_queue # Run with specific connection and domain php artisan queue:work redis --domain=site1.com --queue=site1_queue --tries=3 ``` ## Storage Link Configuration ### Configure Multiple Storage Links Set up domain-specific public storage links for serving files from different tenant storage directories. ```bash # Create symbolic links for each domain's public storage ln -s storage/site1_com/app/public public/storage-site1_com ln -s storage/site2_com/app/public public/storage-site2_com ``` ```php <?php // .env.site1.com // APP_PUBLIC_STORAGE=-site1_com // .env.site2.com // APP_PUBLIC_STORAGE=-site2_com // config/filesystems.php return [ 'disks' => [ 'public' => [ 'driver' => 'local', 'root' => storage_path('app/public'), 'url' => env('APP_URL') . '/storage' . env('APP_PUBLIC_STORAGE', ''), 'visibility' => 'public', ], ], ]; // Usage in application $url = Storage::disk('public')->url('uploads/image.jpg'); // Returns: "https://site1.com/storage-site1_com/uploads/image.jpg" ``` ## Environment File Organization ### Store Environment Files in Custom Directory Configure the package to store all domain environment files in a dedicated subdirectory for better organization. ```php <?php // bootstrap/app.php use Gecche\Multidomain\Foundation\Application; use Illuminate\Foundation\Configuration\Exceptions; use Illuminate\Foundation\Configuration\Middleware; // Store all .env files in the 'envs' subdirectory $environmentPath = dirname(__DIR__) . DIRECTORY_SEPARATOR . 'envs'; $domainParams = []; return Application::configure( basePath: dirname(__DIR__), environmentPath: $environmentPath, domainParams: $domainParams ) ->withRouting( web: __DIR__.'/../routes/web.php', commands: __DIR__.'/../routes/console.php', health: '/up', ) ->withMiddleware(function (Middleware $middleware) { // }) ->withExceptions(function (Exceptions $exceptions) { // })->create(); // Directory structure: // /var/www/app/ // ├── envs/ // │ ├── .env (default) // │ ├── .env.site1.com // │ └── .env.site2.com // ├── storage/ // │ ├── site1_com/ // │ └── site2_com/ // └── ... ``` ## Laravel Horizon Integration ### Configure Horizon for Multi-Domain Integrate Laravel Horizon with the multi-domain package for monitoring queue workers across all tenants. ```php <?php // app/Providers/HorizonServiceProvider.php namespace App\Providers; // Replace default Horizon service provider // use Laravel\Horizon\HorizonApplicationServiceProvider; use Gecche\Multidomain\Horizon\HorizonApplicationServiceProvider; class HorizonServiceProvider extends HorizonApplicationServiceProvider { public function boot(): void { parent::boot(); // Configure Horizon authorization // Horizon::auth(function ($request) { // return true; // }); } } ``` ## Summary Laravel Multidomain provides a comprehensive solution for running multi-tenant applications where each tenant operates under a different HTTP domain. The package seamlessly handles domain detection, environment file loading, storage path separation, and queue isolation. Key use cases include SaaS applications serving multiple customers, white-label solutions with custom domains, and development environments requiring isolated configurations per domain. Integration follows Laravel conventions with minimal configuration changes. Replace the Application class in `bootstrap/app.php`, override the QueueServiceProvider, and use artisan commands to manage domains. The package supports custom domain detection functions, organized environment file storage, and works with Laravel Horizon for queue monitoring. All artisan commands accept the `--domain` option for domain-specific execution, making it straightforward to run migrations, clear caches, or process queues for individual tenants.