Laravel: Maintenance Mode [proper usage with internal working]

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-

Running database migrations.
Performing other data manipulation tasks, like syncing, restructuring, and bulk deletion of multiple table data.
Any other database or server maintenance work.
Application packages setup or upgrade.
Any debugging work, that is crucial in production and we don’t want the user to see the debug messages.

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-

bigboxcode demo site
BigBoxCode demo site

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-

Laravel enable maintenance mode
Laravel enable maintenance mode

After that, check the site. You will see output like below-

Laravel Maintenance mode - Service Unavailable
Laravel Maintenance mode – Service Unavailable

Check the header, it returns HTTP status 503 (Service unavailable).

Laravel maintenance mode response headers
Laravel maintenance mode response headers

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-

Laravel Maintenance mode disable
Laravel Maintenance mode disable

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.

–redirect : define the path to which the user would be redirected to.
–render : If we want to provide a custom view to render in maintenance mode.
–refresh : Auto refresh after certain seconds
–secret : a string that we can use with the URL to bypass the maintenance mode. The site will still be in maintenance mode, but we can see output by passing this secret string.
–status : Define an HTTP status other than 503 to be returned in maintenance mode.

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.

Laravel maintenance mode response headers with refresh
Laravel maintenance mode response headers with refresh

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.

Laravel maintenance mode response headers with redirect
Laravel maintenance mode response headers with redirect

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.

Laravel maintenance mode response headers with secret
Laravel maintenance mode response headers with secret

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.

Laravel maintenance mode response headers with different status
Laravel maintenance mode response headers with different status

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.

Custom service down message
Custom service down message

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)
Laravel Maintenance Mode Configuration
Laravel Maintenance Mode Configuration

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-

Set “driver” to “cache
Set “store” to “database

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-

keyvalueexpiration
illuminate:foundation:downa: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-

Set “driver” to “cache
Set “store” to “redis

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

Laravel Kernel Middleware
Laravel Kernel Middleware
<?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;
    }
}

Leave a Comment


The reCAPTCHA verification period has expired. Please reload the page.