Enabling maintenance mode allows us to show nice and use friendly messages when our application is going through some maintenance work under the hood, and we are not able to serve.
We need to activate the maintenance mode of our web application while-
Enabling maintenance mode in Laravel is easy, and very well designed. Let’s see how we can enable maintenance mode in Larave and what options we have.
Here is our simple Laravel application-
Enable Maintenance Mode
Let’s see how we can enable maintenance mode. It is very simple, use the following artisan command in the terminal-
php artisan down
On successful execution of the command, we will get the following message in the terminal-
After that, check the site. You will see output like below-
Check the header, it returns HTTP status 503 (Service unavailable).
Disable Maintenance Mode
To put the site back live, just use the following artisan command-
php artisan up
We will get the following output in the terminal-
It means the site is live and we can use the site again.
Artisan “down” Command Details
Let’s check the details of the artisan “down” command and the options available-
$ php artisan down --help
Description:
Put the application into maintenance / demo mode
Usage:
down [options]
Options:
--redirect[=REDIRECT] The path that users should be redirected to
--render[=RENDER] The view that should be prerendered for display during maintenance mode
--retry[=RETRY] The number of seconds after which the request may be retried
--refresh[=REFRESH] The number of seconds after which the browser may refresh
--secret[=SECRET] The secret phrase that may be used to bypass maintenance mode
--status[=STATUS] The status code that should be used when returning the maintenance mode response [default: "503"]
-h, --help Display help for the given command. When no command is given display help for the list command
-q, --quiet Do not output any message
-V, --version Display this application version
--ansi|--no-ansi Force (or disable --no-ansi) ANSI output
-n, --no-interaction Do not ask any interactive question
--env[=ENV] The environment the command should run under
-v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
Here are the important options and what these options refer to. All these options are optional and should be used only when necessary.
We have discussed the options in detail below.
Enable Auto Refresh in Maintenance Mode
Let’s set the refresh option for maintenance mode. The client will refresh automatically after a certain time, if this option is set. This way the user does not have to refresh manually and when the site is back, it will reflect on the client.
We can use the –refresh flag for this. The time is set in seconds. Let’s set a 10-second refresh duration when the site is in maintenance mode. Use the following command-
php artisan down --refresh=10
Open the application in a web browser. We will see the maintenance mode message on the site. But wait for 10 seconds, and it will refresh automatically.
Check the header of the response, and you will see a “Refresh” header with the value 10. This asks browsers/clients to refresh the page every 10 seconds.
Enable Redirect in Maintenance Mode
We can set a path, where the user will be redirected when application is in maintenance mode.
Use the flag –redirect for that. For example, use the command below-
php artisan down --redirect=https://bigboxcode.com
Open the site in your browser, and check the headers.
You will see the status code 302, and “Location” header set to the path you provided in the command. The client will be redirected to the provided path.
Bypass Maintenance Mode Using Secret
Say, we want the general users to see the maintenance mode message, but we want to see the site output a the same time. We want to make sure the site is working as expected before making it public, the the recent changes.
We can set a token or secret string while enabling the maintenance mode. Use the command below-
php artisan down --secret="mybigboxsecret"
This will set the string “mybigboxsecret” and the secret token. We can use the secret to access our site.
Like https://bigboxcode.com/mybigboxsecret, This will enable access for the user and redirect to the original site.
After this, the user can browse the site normally, even if the site is in maintenance mode.
Use this to evaluate the output after a big change.
Enable Maintenance Mode with Different HTTP Status
If you want to send some HTTP status in response, other than 503, Then use the –status option. Use the command below, here we are sending HTTP status code 504 for example-
php artisan down --status=504
Check the headers in the response, you will see the status code that you set while enabling maintenance mode using –status option.
Maintenance Mode with Custom View
Create a view file in /resources/views with your preferred name. We have created a view file /resources/views/service-down.blade.php, and added the following HTML code-
<h1>Service down for maintenance</h1>
<p>Service is down for maintenance. We will be back soon. Please check later.</p>
Use the –render option and pass the name of the view file, like below-
php artisan down --render="service-down"
Check the site, you will see the view that you passed in –render option. For example, our output will be like below as per the HTML we used above.
Maintenace Mode Configuration
We can set maintenance mode in 2 ways-
- File-Based Maintenance Mode(Default)
- Cache-Based Maintenace Mode(up/down status shared between multiple servers)
Let’s see how can we use each of these modes.
File-Based Maintenance Mode
In File-Based Maintenance mode the information of the maintenance mode is saved in a file.
To use the File-Based mode, find the config for “maintenance
” in the file config/app.php
. In the “maintenance” config set "driver" => "file"
.
Here is the configuration-
<?php
// config\app.php
use Illuminate\Support\Facades\Facade;
return [
// Other config for the application
/*
|--------------------------------------------------------------------------
| Maintenance Mode Driver
|--------------------------------------------------------------------------
|
| These configuration options determine the driver used to determine and
| manage Laravel's "maintenance mode" status. The "cache" driver will
| allow maintenance mode to be controlled across multiple machines.
|
| Supported drivers: "file", "cache"
|
*/
'maintenance' => [
'driver' => 'file',
],
// Other config for the application
];
Now, when you put your application to maintenance mode, the following things will happen-
- A file is created at
storage\framework\down
, the file name is “down
“. It stores all the options and config that you set for maintenance mode configuration. - A file is created at
storage\framework
the path named “maintenance.php
“
The application checks the storage\framework\down
file to get all the options for maintenance mode.
WARNING
As the configuration is saved in a file, so this is available on that server only.
If you have a multiserver distributed setup, or multiple instances of container running with the same Laravel application(probably with a load balancer in the front) then other servers don’t have this information, and maintenance mode will not be enabled for the other servers/containers.
Use this only when you have a single server setup.
Cache-Based Maintenance Mode
In cache based Maintenance mode, we can use cache drivers with different storages, like-
- Database
- Redis server
NOTE
Cache-based information is suitable for multi-server setup.
As the information is saved in a central storage like a database or Redis server. so all the servers can use this info.
** This can be used with a single server config also.
Cache Driver with Database as Storage
To store maintenance mode information using the database as storage-
The maintenance configuration in our config\app.php
file will look like below-
<?php
// config\app.php
use Illuminate\Support\Facades\Facade;
return [
// Other config for the app
/*
|--------------------------------------------------------------------------
| Maintenance Mode Driver
|--------------------------------------------------------------------------
|
| These configuration options determine the driver used to determine and
| manage Laravel's "maintenance mode" status. The "cache" driver will
| allow maintenance mode to be controlled across multiple machines.
|
| Supported drivers: "file", "cache"
|
*/
'maintenance' => [
'driver' => 'cache',
'store' => 'database',
],
// Other config for the app
];
Now if we enable maintenance mode, the maintenance mode information will be saved in the database(that we have configured for our Laravel app). You can check the information in the table named “caches“.
Table Name: caches
The data looks like below-
key | value | expiration |
---|---|---|
illuminate:foundation:down | a:7:{s:6:”except”;a:0:{}s:8:”redirect”;N;s:5:”retry”;N;s:7:”refresh”;N;s:6:”secret”;N;s:6:”status”;i:503;s:8:”template”;N;} | 2029826443 |
Cache Driver with Redis as Storage
To store maintenance mode information using the redis as storage-
The maintenance configuration in our config\app.php
file will look like below-
<?php
// config\app.php
use Illuminate\Support\Facades\Facade;
return [
// Other config for the app
/*
|--------------------------------------------------------------------------
| Maintenance Mode Driver
|--------------------------------------------------------------------------
|
| These configuration options determine the driver used to determine and
| manage Laravel's "maintenance mode" status. The "cache" driver will
| allow maintenance mode to be controlled across multiple machines.
|
| Supported drivers: "file", "cache"
|
*/
'maintenance' => [
'driver' => 'cache',
'store' => 'redis',
],
// Other config for the app
];
Make sure that you have Redis server running and PHP packages like “phpredis” or “predis” is installed. Then set Redis config in the .env file-
# .env
# Other Laravel cofnig
# REDIS_CLIENT=phpredis
REDIS_CLIENT=predis
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
# Other laravel config
To check where the data is stored, first check the Redis configuration in the config\database.php file-
<?php
// config\database.php
use Illuminate\Support\Str;
return [
// ... Other database configurations here
/*
|--------------------------------------------------------------------------
| Redis Databases
|--------------------------------------------------------------------------
|
| Redis is an open source, fast, and advanced key-value store that also
| provides a richer body of commands than a typical key-value system
| such as APC or Memcached. Laravel makes it easy to dig right in.
|
*/
'redis' => [
'client' => env('REDIS_CLIENT', 'phpredis'),
'options' => [
'cluster' => env('REDIS_CLUSTER', 'redis'),
'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_database_'),
],
'default' => [
'url' => env('REDIS_URL'),
'host' => env('REDIS_HOST', '127.0.0.1'),
'username' => env('REDIS_USERNAME'),
'password' => env('REDIS_PASSWORD'),
'port' => env('REDIS_PORT', '6379'),
'database' => env('REDIS_DB', '0'),
],
'cache' => [
'url' => env('REDIS_URL'),
'host' => env('REDIS_HOST', '127.0.0.1'),
'username' => env('REDIS_USERNAME'),
'password' => env('REDIS_PASSWORD'),
'port' => env('REDIS_PORT', '6379'),
'database' => env('REDIS_CACHE_DB', '1'),
],
],
];
NOTE
Specially check the config for “cache” inside “redis”.
Most of the config is coming from .env.
Check the database part, here we have the database set from “REDIS_CACHE_DB” or default to “1”.
So Redis is using the database with index “1” for cache (as we have not set the “REDIS_CACHE_DB”).
Name of the key for the down information will be like this – yourappname_database_illuminate:foundation:down
Check the maintenance mode values in Redis-
# Access Redis using redis-cli
$ redis-cli
127.0.0.1:6379>
# Select database 1, as we are using database 1 for caching
127.0.0.1:6379> select 1
OK
# Check the keys
127.0.0.1:6379[1]> keys *
1) "bigboxcode_database_illuminate:foundation:down"
# You will see a key for the down configuration
# Check the value of the down configuration
127.0.0.1:6379[1]> get bigboxcode_database_illuminate:foundation:down
"a:7:{s:6:\"except\";a:1:{i:0;s:7:\"site-up\";}s:8:\"redirect\";N;s:5:\"retry\";N;s:7:\"refresh\";N;s:6:\"secret\";N;s:6:\"status\";i:503;s:8:\"template\";N;}"
# Check the type- type is string. It is a JSON encode value as string
127.0.0.1:6379[1]> type bigboxcode_database_illuminate:foundation:down
string
Ignore/Exclude Some Path from Maintenance Mode
In some situations, we want to ignore some paths from the maintenance mode. Like, we want to access the admin panel, or some specific URL when the site is in maintenance mode.
To do that we need to use a middleware. Check if there is a middleware present in your App/Http/Middleware directory named “PreventRequestsDuringMaintenance
“.
If not present then create a middleware using the following command-
php artisan make:middleware PreventRequestsDuringMaintenance
Add the following code in the middleware “PreventRequestsDuringMaintenance
“-
<?php
// app\Http\Middleware\PreventRequestsDuringMaintenance.php
namespace App\Http\Middleware;
use Illuminate\Foundation\Http\Middleware\PreventRequestsDuringMaintenance as Middleware;
class PreventRequestsDuringMaintenance extends Middleware
{
/**
* The URIs that should be reachable while maintenance mode is enabled.
*
* @var array<int, string>
*/
protected $except = [
'admin',
'some-path-2',
'some-custom-path',
];
}
The main implementation is in the file vendor\laravel\framework\src\Illuminate\Foundation\Http\Middleware\PreventRequestsDuringMaintenance.php
We are just extending Illuminate\Foundation\Http\Middleware\PreventRequestsDuringMaintenance
in our middleware and setting the $except
property.
Maintenance Mode by Calling Route
In some situation, you don’t want to access the artisan command from the command line for enabling/disabling maintenance mode.
We can set up routes and then call the artisan “down” command from it. This way we can enable or disable the maintenance mode just by calling the URL from the browser.
Let’s create a route for enabling maintenance mode-
Route::get('site-down', function() {
Artisan::call('down', [
'--refresh' => 100
]);
echo Artisan::output();
});
Define another route to make the site up-
Route::get('site-up', function() {
Artisan::call('up');
echo Artisan::output();
});
WARNING
Make sure to add the “site-up” route in the exclude path of maintenance mode. Use the process mentioned above to exclude certain paths from maintenance mode.
Use some token or some complex path for this, so that someone can not find this URL easily.
Maintenance Mode Internals
Let’s see, what happens internally when we type “php artisan down“?
Artisan “down” command
First take a look at the code in the file “vendor\laravel\framework\src\Illuminate\Foundation\Console\DownCommand.php
“. Specifically, check the “handle
” function.
<?php
// vendor\laravel\framework\src\Illuminate\Foundation\Console\DownCommand.php
namespace Illuminate\Foundation\Console;
use App\Http\Middleware\PreventRequestsDuringMaintenance;
use Exception;
use Illuminate\Console\Command;
use Illuminate\Foundation\Events\MaintenanceModeEnabled;
use Illuminate\Foundation\Exceptions\RegisterErrorViewPaths;
use Symfony\Component\Console\Attribute\AsCommand;
use Throwable;
#[AsCommand(name: 'down')]
class DownCommand extends Command
{
/**
* The console command signature.
*
* @var string
*/
protected $signature = 'down {--redirect= : The path that users should be redirected to}
{--render= : The view that should be prerendered for display during maintenance mode}
{--retry= : The number of seconds after which the request may be retried}
{--refresh= : The number of seconds after which the browser may refresh}
{--secret= : The secret phrase that may be used to bypass maintenance mode}
{--status=503 : The status code that should be used when returning the maintenance mode response}';
/**
* The name of the console command.
*
* This name is used to identify the command during lazy loading.
*
* @var string|null
*
* @deprecated
*/
protected static $defaultName = 'down';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Put the application into maintenance / demo mode';
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
try {
if ($this->laravel->maintenanceMode()->active()) {
$this->components->info('Application is already down.');
return 0;
}
$this->laravel->maintenanceMode()->activate($this->getDownFilePayload());
file_put_contents(
storage_path('framework/maintenance.php'),
file_get_contents(__DIR__.'/stubs/maintenance-mode.stub')
);
$this->laravel->get('events')->dispatch(new MaintenanceModeEnabled());
$this->components->info('Application is now in maintenance mode.');
} catch (Exception $e) {
$this->components->error(sprintf(
'Failed to enter maintenance mode: %s.',
$e->getMessage(),
));
return 1;
}
}
// Other code for managing maintenance mode processing
}
<?php
// vendor\laravel\framework\src\Illuminate\Foundation\Console\DownCommand.php
namespace Illuminate\Foundation\Console;
use App\Http\Middleware\PreventRequestsDuringMaintenance;
use Exception;
use Illuminate\Console\Command;
use Illuminate\Foundation\Events\MaintenanceModeEnabled;
use Illuminate\Foundation\Exceptions\RegisterErrorViewPaths;
use Symfony\Component\Console\Attribute\AsCommand;
use Throwable;
#[AsCommand(name: 'down')]
class DownCommand extends Command
{
// ... handle function
/**
* Get the payload to be placed in the "down" file.
*
* @return array
*/
protected function getDownFilePayload()
{
return [
'except' => $this->excludedPaths(),
'redirect' => $this->redirectPath(),
'retry' => $this->getRetryTime(),
'refresh' => $this->option('refresh'),
'secret' => $this->option('secret'),
'status' => (int) $this->option('status', 503),
'template' => $this->option('render') ? $this->prerenderView() : null,
];
}
/**
* Get the paths that should be excluded from maintenance mode.
*
* @return array
*/
protected function excludedPaths()
{
try {
return $this->laravel->make(PreventRequestsDuringMaintenance::class)->getExcludedPaths();
} catch (Throwable $e) {
return [];
}
}
/**
* Get the path that users should be redirected to.
*
* @return string
*/
protected function redirectPath()
{
if ($this->option('redirect') && $this->option('redirect') !== '/') {
return '/'.trim($this->option('redirect'), '/');
}
return $this->option('redirect');
}
/**
* Prerender the specified view so that it can be rendered even before loading Composer.
*
* @return string
*/
protected function prerenderView()
{
(new RegisterErrorViewPaths)();
return view($this->option('render'), [
'retryAfter' => $this->option('retry'),
])->render();
}
/**
* Get the number of seconds the client should wait before retrying their request.
*
* @return int|null
*/
protected function getRetryTime()
{
$retry = $this->option('retry');
return is_numeric($retry) && $retry > 0 ? (int) $retry : null;
}
}
<?php
// vendor\laravel\framework\src\Illuminate\Foundation\MaintenanceModeManager.php
namespace Illuminate\Foundation;
use Illuminate\Support\Manager;
class MaintenanceModeManager extends Manager
{
/**
* Create an instance of the file based maintenance driver.
*
* @return \Illuminate\Foundation\FileBasedMaintenanceMode
*/
protected function createFileDriver(): FileBasedMaintenanceMode
{
return new FileBasedMaintenanceMode();
}
/**
* Create an instance of the cache based maintenance driver.
*
* @return \Illuminate\Foundation\CacheBasedMaintenanceMode
*
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/
protected function createCacheDriver(): CacheBasedMaintenanceMode
{
return new CacheBasedMaintenanceMode(
$this->container->make('cache'),
$this->config->get('app.maintenance.store') ?: $this->config->get('cache.default'),
'illuminate:foundation:down'
);
}
/**
* Get the default driver name.
*
* @return string
*/
public function getDefaultDriver(): string
{
return $this->config->get('app.maintenance.driver', 'file');
}
}
<?php
// vendor\laravel\framework\src\Illuminate\Foundation\FileBasedMaintenanceMode.php
namespace Illuminate\Foundation;
use Illuminate\Contracts\Foundation\MaintenanceMode as MaintenanceModeContract;
class FileBasedMaintenanceMode implements MaintenanceModeContract
{
/**
* Take the application down for maintenance.
*
* @param array $payload
* @return void
*/
public function activate(array $payload): void
{
file_put_contents(
$this->path(),
json_encode($payload, JSON_PRETTY_PRINT)
);
}
/**
* Take the application out of maintenance.
*
* @return void
*/
public function deactivate(): void
{
if ($this->active()) {
unlink($this->path());
}
}
/**
* Determine if the application is currently down for maintenance.
*
* @return bool
*/
public function active(): bool
{
return file_exists($this->path());
}
/**
* Get the data array which was provided when the application was placed into maintenance.
*
* @return array
*/
public function data(): array
{
return json_decode(file_get_contents($this->path()), true);
}
/**
* Get the path where the file is stored that signals that the application is down for maintenance.
*
* @return string
*/
protected function path(): string
{
return storage_path('framework/down');
}
}
vendor\laravel\framework\src\Illuminate\Foundation\Console\stubs\maintenance-mode.stub
<?php
// storage\framework\maintenance.php
// Check if the application is in maintenance mode...
if (! file_exists($down = __DIR__.'/down')) {
return;
}
// Decode the "down" file's JSON...
$data = json_decode(file_get_contents($down), true);
// Allow framework to handle request if no prerendered template...
if (! isset($data['template'])) {
return;
}
// Allow framework to handle request if request URI is in the exclude list...
if (isset($data['except'])) {
$uri = parse_url($_SERVER['REQUEST_URI'])['path'];
$uri = rawurldecode($uri !== '/' ? trim($uri, '/') : $uri);
foreach ((array) $data['except'] as $except) {
$except = $except !== '/' ? trim($except, '/') : $except;
if ($except == $uri) {
return;
}
$except = preg_quote($except, '#');
$except = str_replace('\*', '.*', $except);
if (preg_match('#^'.$except.'\z#u', $uri) === 1) {
return;
}
}
}
// Allow framework to handle maintenance mode bypass route...
if (isset($data['secret']) && $_SERVER['REQUEST_URI'] === '/'.$data['secret']) {
return;
}
// Determine if maintenance mode bypass cookie is valid...
if (isset($_COOKIE['laravel_maintenance']) && isset($data['secret'])) {
$payload = json_decode(base64_decode($_COOKIE['laravel_maintenance']), true);
if (is_array($payload) &&
is_numeric($payload['expires_at'] ?? null) &&
isset($payload['mac']) &&
hash_equals(hash_hmac('sha256', $payload['expires_at'], $data['secret']), $payload['mac']) &&
(int) $payload['expires_at'] >= time()) {
return;
}
}
// Redirect to the proper path if necessary...
if (isset($data['redirect']) && $_SERVER['REQUEST_URI'] !== $data['redirect']) {
http_response_code(302);
header('Location: '.$data['redirect']);
exit;
}
// Output the prerendered template...
http_response_code($data['status'] ?? 503);
if (isset($data['retry'])) {
header('Retry-After: '.$data['retry']);
}
if (isset($data['refresh'])) {
header('Refresh: '.$data['refresh']);
}
echo $data['template'];
exit;
storage\framework\down
{
"except": [],
"redirect": null,
"retry": null,
"refresh": null,
"secret": null,
"status": 503,
"template": null
}
<?php
// vendor\laravel\framework\src\Illuminate\Foundation\CacheBasedMaintenanceMode.php
namespace Illuminate\Foundation;
use Illuminate\Contracts\Cache\Factory;
use Illuminate\Contracts\Cache\Repository;
use Illuminate\Contracts\Foundation\MaintenanceMode;
class CacheBasedMaintenanceMode implements MaintenanceMode
{
/**
* The cache factory.
*
* @var \Illuminate\Contracts\Cache\Factory
*/
protected $cache;
/**
* The cache store that should be utilized.
*
* @var string
*/
protected $store;
/**
* The cache key to use when storing maintenance mode information.
*
* @var string
*/
protected $key;
/**
* Create a new cache based maintenance mode implementation.
*
* @param \Illuminate\Contracts\Cache\Factory $cache
* @param string $store
* @param string $key
* @return void
*/
public function __construct(Factory $cache, string $store, string $key)
{
$this->cache = $cache;
$this->store = $store;
$this->key = $key;
}
/**
* Take the application down for maintenance.
*
* @param array $payload
* @return void
*/
public function activate(array $payload): void
{
$this->getStore()->put($this->key, $payload);
}
/**
* Take the application out of maintenance.
*
* @return void
*/
public function deactivate(): void
{
$this->getStore()->forget($this->key);
}
/**
* Determine if the application is currently down for maintenance.
*
* @return bool
*/
public function active(): bool
{
return $this->getStore()->has($this->key);
}
/**
* Get the data array which was provided when the application was placed into maintenance.
*
* @return array
*/
public function data(): array
{
return $this->getStore()->get($this->key);
}
/**
* Get the cache store to use.
*
* @return \Illuminate\Contracts\Cache\Repository
*/
protected function getStore(): Repository
{
return $this->cache->store($this->store);
}
}
<?php
// public\index.php
// ... other code in the file
if (file_exists($maintenance = __DIR__.'/../storage/framework/maintenance.php')) {
require $maintenance;
}
// ... other code in the file for live mode site
PreventRequestsDuringMaintenance Middleware in Kernel
<?php
// vendor\laravel\framework\src\Illuminate\Foundation\Http\Middleware\PreventRequestsDuringMaintenance.php
namespace Illuminate\Foundation\Http\Middleware;
use Closure;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Foundation\Http\MaintenanceModeBypassCookie;
use Symfony\Component\HttpKernel\Exception\HttpException;
class PreventRequestsDuringMaintenance
{
/**
* The application implementation.
*
* @var \Illuminate\Contracts\Foundation\Application
*/
protected $app;
/**
* The URIs that should be accessible while maintenance mode is enabled.
*
* @var array<int, string>
*/
protected $except = [];
/**
* Create a new middleware instance.
*
* @param \Illuminate\Contracts\Foundation\Application $app
* @return void
*/
public function __construct(Application $app)
{
$this->app = $app;
}
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*
* @throws \Symfony\Component\HttpKernel\Exception\HttpException
*/
public function handle($request, Closure $next)
{
if ($this->app->maintenanceMode()->active()) {
$data = $this->app->maintenanceMode()->data();
if (isset($data['secret']) && $request->path() === $data['secret']) {
return $this->bypassResponse($data['secret']);
}
if ($this->hasValidBypassCookie($request, $data) ||
$this->inExceptArray($request)) {
return $next($request);
}
if (isset($data['redirect'])) {
$path = $data['redirect'] === '/'
? $data['redirect']
: trim($data['redirect'], '/');
if ($request->path() !== $path) {
return redirect($path);
}
}
if (isset($data['template'])) {
return response(
$data['template'],
$data['status'] ?? 503,
$this->getHeaders($data)
);
}
throw new HttpException(
$data['status'] ?? 503,
'Service Unavailable',
null,
$this->getHeaders($data)
);
}
return $next($request);
}
/**
* Determine if the incoming request has a maintenance mode bypass cookie.
*
* @param \Illuminate\Http\Request $request
* @param array $data
* @return bool
*/
protected function hasValidBypassCookie($request, array $data)
{
return isset($data['secret']) &&
$request->cookie('laravel_maintenance') &&
MaintenanceModeBypassCookie::isValid(
$request->cookie('laravel_maintenance'),
$data['secret']
);
}
/**
* Determine if the request has a URI that should be accessible in maintenance mode.
*
* @param \Illuminate\Http\Request $request
* @return bool
*/
protected function inExceptArray($request)
{
foreach ($this->getExcludedPaths() as $except) {
if ($except !== '/') {
$except = trim($except, '/');
}
if ($request->fullUrlIs($except) || $request->is($except)) {
return true;
}
}
return false;
}
/**
* Redirect the user back to the root of the application with a maintenance mode bypass cookie.
*
* @param string $secret
* @return \Illuminate\Http\RedirectResponse
*/
protected function bypassResponse(string $secret)
{
return redirect('/')->withCookie(
MaintenanceModeBypassCookie::create($secret)
);
}
/**
* Get the headers that should be sent with the response.
*
* @param array $data
* @return array
*/
protected function getHeaders($data)
{
$headers = isset($data['retry']) ? ['Retry-After' => $data['retry']] : [];
if (isset($data['refresh'])) {
$headers['Refresh'] = $data['refresh'];
}
return $headers;
}
/**
* Get the URIs that should be accessible even when maintenance mode is enabled.
*
* @return array
*/
public function getExcludedPaths()
{
return $this->except;
}
}