carteni/maintenance-bundle
Symfony bundle to put your site into maintenance mode while allowing access for a whitelist of IP addresses. Configure via YAML or XML, optionally route to a custom controller, and override the maintenance Twig templates to match your app.
Install the package via Composer:
composer require carteni/maintenance-bundle
Enable the bundle in config/app.php (Laravel 5.5+) or AppKernel.php (older versions):
'providers' => [
// ...
Mes\Misc\MaintenanceBundle\MesMaintenanceBundle::class,
],
Configure maintenance mode in config/packages/mes_maintenance.yaml (or config/mes_maintenance.php if using PHP config):
mes_maintenance:
enabled: true
ips_allowed: [127.0.0.1, 192.168.1.100] # Replace with your allowed IPs
controller: "" # Leave blank for default maintenance page
Override the maintenance template (optional):
Copy vendor/carteni/maintenance-bundle/resources/views/index.html.twig to resources/views/vendor/mes_maintenance/index.html.twig and customize.
Test maintenance mode:
Deploying a quick maintenance page during a critical update:
enabled: true in config.ips_allowed.php artisan config:clear if changes don’t apply.allowed_ips table) and fetch them dynamically in a custom config loader:
// app/Providers/MesMaintenanceServiceProvider.php
public function boot()
{
$ips = DB::table('allowed_ips')->pluck('ip')->toArray();
config(['mes_maintenance.ips_allowed' => $ips]);
}
.env):
# config/packages/mes_maintenance.yaml
mes_maintenance:
enabled: "%env(bool:MAINTENANCE_MODE,false)%"
echo "MAINTENANCE_MODE=true" >> .env && php artisan config:clear
mes_maintenance:
controller: "App\\Http\\Controllers\\MaintenanceController@showCustomPage"
public function showCustomPage(Request $request)
{
if ($request->is('admin/*')) {
return view('admin.maintenance');
}
return view('public.maintenance');
}
public function handle($request, Closure $next)
{
if (config('mes_maintenance.enabled') &&
!in_array($request->ip(), config('mes_maintenance.ips_allowed'))) {
return redirect()->route('maintenance.page');
}
return $next($request);
}
app/Http/Kernel.php:
protected $middleware = [
\App\Http\Middleware\CheckMaintenance::class,
];
mes_maintenance:
enabled: true
ips_allowed: [...]
bypass_for_paths: ["/api/*"] # Custom config key
Pre-deployment:
enabled: true).ips_allowed.During deployment:
php artisan config:clear, php artisan cache:clear).Post-deployment:
enabled: false).ips_allowed (or add others).ips_allowed.MaintenanceModeEnabled) for logging or notifications:
// In a listener
public function handle()
{
Log::info('Maintenance mode enabled. Allowed IPs: ' . implode(', ', config('mes_maintenance.ips_allowed')));
}
php artisan maintenance:enable --ips="192.168.1.1,127.0.0.1"
<meta name="maintenance-mode" content="{{ app.maintenanceMode ? 'true' : 'false' }}">
if (document.querySelector('meta[name="maintenance-mode"]').content === 'true') {
window.location.href = '/maintenance';
}
Request object to test IP checks:
$request = new Request(['ip' => '192.168.1.100']);
$this->assertFalse(MaintenanceChecker::isInMaintenance($request));
$response = $this->get('/');
$response->assertStatus(200); // Allowed IP
$this->actingAsUserWithIp('1.2.3.4')->get('/')->assertRedirect('/maintenance');
192.168.1.0/24) won’t work by default.
Fix: Use a library like digitalbush/ip-range to parse CIDR notation:
$ips = IpRange::parse(config('mes_maintenance.ips_allowed'));
$isAllowed = $ips->contains($request->ip());
127.0.0.1 and ::1 (IPv6) may not work as expected in shared hosting.
Fix: Add all variants:
ips_allowed: [127.0.0.1, ::1, 127.0.1.1]
$ips = Cache::remember('mes_maintenance.ips', 60, function () {
return config('mes_maintenance.ips_allowed');
});
ips_allowed, run:
php artisan config:clear
maintenance middleware, conflicts may arise.
Fix: Disable Laravel’s middleware and rely solely on this bundle:
// app/Http/Kernel.php
protected $middlewareGroups = [
'web' => [
// Remove \Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class
],
];
resources/views/vendor/mes_maintenance/ may not load.
Fix: Ensure the bundle’s service provider publishes assets:
// In MesMaintenanceBundle ServiceProvider
public function boot()
{
$this->publishes([
__DIR__.'/../resources/views' => resource_path('views/vendor/mes_maintenance'),
], 'mes-maintenance-views');
}
Then run:
php artisan vendor:publish --tag=mes-maintenance-views
How can I help you explore Laravel packages today?