1. 基本
  2. IP制限
  3. 参考

基本

Laravel 5は、標準でメンテナンスモードへの切り替え、解除機能をもっています。 メンテナンスモードを有効にすると、全てのアクセスで503エラーとなり、以下のテンプレートを表示します。

resources/views/errors/503.blade.php

メンテナンスモードかどうかの判定は、以下のファイルで行っています。

storage/framework/down
参考 メンテナンスモードの判定について

上記ファイルが存在するとメンテナンスモードとなります。 判定はミドルウェアで行われていて、app/Http/Kernel.php の $middlewareで定義されている以下になります。

\Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class

IP制限

サイトを運用していると、許可IPだけに正常画面を表示し、許可IP以外にはメンテナンス画面を表示したい場合があります。 しかし、デフォルトではできないので、許可IPは通常画面を表示して、それ以外はメンテナンス画面を表示するミドルウェアを作成します。 また、許可IPは、簡単に変更できるように.envファイルで設定するようにします。

ミドルウェアは、もともと使用されている以下のファイルを参考にします。

vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/CheckForMaintenanceMode.php

  • 許可IPの指定方法

  • .envに新しい項目を追加します。今回は変数名を"MAINTENANCE_IP"として、許可IPをカンマ区切りで複数指定できるようにします。

    例 .envに追加
    // メンテナンス時にアクセス可能なIP
    // カンマ区切りで定義する
    MAINTENANCE_IP=192.0.0.10,172.16.1.10
    ※許可IPを指定しない場合は、上記を削除するか、"//"でコメントアウトしておきます。

  • 作成

  • Laravel5のミドルウェアの雛形は コマンド "artisan make:middleware"で作成できます。 今回は、元のファイルと同じ名前の"CheckForMaintenanceMode"にするので、以下のように実行しました。

    $ php artisan make:middleware CheckForMaintenanceMode
    Middleware created successfully.
    すると、以下のようなファイルが作成されます。
    app/Http/Middleware/CheckForMaintenanceMode.php
    中身は以下のようになっています。
    <?php
    
    namespace App\Http\Middleware;
    
    use Closure;
    
    class CheckForMaintenanceMode
    {
        /**
         * Handle an incoming request.
         *
         * @param  \Illuminate\Http\Request  $request
         * @param  \Closure  $next
         * @return mixed
         */
        public function handle($request, Closure $next)
        {
            return $next($request);
        }
    }

    これを以下のように修正して、メンテナンスモードの時、.envの"MAINTENANCE_IP"をチェックするようにします。 アクセスしてきたクライアントIPの取得は、$request->getClientIp()で行っています。

    <?php
    
    namespace App\Http\Middleware;
    
    use Closure;
    use Illuminate\Contracts\Foundation\Application;
    use Symfony\Component\HttpKernel\Exception\HttpException;
    
    /**
     * オリジナルは以下
     * Illuminate\Foundation\Http\Middleware\CheckForMainenanceMode.php
     *
     */
    class CheckForMaintenanceMode
    {
        protected $app;
    
        /**
         * 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
         */
        public function handle($request, Closure $next)
        {
            if ($this->app->isDownForMaintenance()) {
                $ip = $request->getClientIp();
                $allowIp = explode(',', env('MAINTENANCE_IP'));
                if (!is_array($allowIp) || !in_array($ip, $allowIp)) {
                    throw new HttpException(503);
                }
            }
            return $next($request);
        }
    }

  • 作成したミドルウェアのユニットテスト

  • ユニットテストを作成するミドルウェアのパスは以下なので、

    app/Http/Middleware/CheckForMaintenanceMode.php
    以下のコマンドを実行して、雛形を作成します。
    $ php artisan make:test Http/Middleware/CheckForMaintenanceModeTest
    参考 Laravel 5 ユニットテスト・雛形作成

    すると、以下のファイルが作成されます。

    tests/Http/Middleware/CheckForMaintenanceModeTest.php

    作成されたファイルを修正して以下のようにします。 メンテナンスモードの切り替えは、storage/framework/down ファイルを直接操作するのではなく、 コマンドラインからの切り替えに使用されている、UpCommandとDownCommandを使いました。 また、許可IPは、app/.envに設定している値を使うので、.envで設定しておく必要があります。

    <?php
    use App\Http\Middleware\CheckForMaintenanceMode;
    use Illuminate\Contracts\Foundation\Application;
    use Illuminate\Http\Request;
    use Illuminate\Foundation\Console\UpCommand;
    use Illuminate\Foundation\Console\DownCommand;
    
    use Symfony\Component\HttpFoundation\ServerBag;
    use Symfony\Component\Console\Input\ArgvInput;
    use Symfony\Component\Console\Output\ConsoleOutput;
    use Symfony\Component\Console\Output\NullOutput;
    
    class CheckForMaintenanceModeTest extends TestCase {
    
        protected $target;
    
        protected $input;
        protected $output;
    
        protected $downCommand;
        protected $upCommand;
    
        public function setUp()
        {
            parent::setUp();
            $this->target = new CheckForMaintenanceMode($this->app);
    
            $laravel = $this->app->make('Illuminate\Contracts\Foundation\Application');
            $application = $this->app->make('Symfony\Component\Console\Application');
    
            $this->upCommand = new UpCommand();
            $this->upCommand->setLaravel($laravel);
            $this->upCommand->setApplication($application);
    
            $this->downCommand = new DownCommand();
            $this->downCommand->setLaravel($laravel);
            $this->downCommand->setApplication($application);
    
            $this->input = new ArgvInput();
            $this->output = new NullOutput();
        }
    
        public function testHandler()
        {
            // 許可IP
            $menteIps= explode(',', env('MAINTENANCE_IP'));
            $allowIp = $menteIps[0];
            $this->assertRegExp('/^\d+\.\d+\.\d+\.\d$/', $allowIp);
    
            // 許可されていないIPアドレス
            $otherIp = '10.254.254.254';
            $this->assertNotEquals($otherIp, $allowIp);
    
            $next = function($request) {
                return 'test ok';
            };
            $request = new Request();
    
            //***********************************
            // メンテナンス解除
            $this->upCommand->run($this->input, $this->output);
    
            // メンテナンスでない場合、許可IP以外からアクセス可能であること
            $request->server = new ServerBag(['REMOTE_ADDR' => $otherIp]);
            $result = $this->target->handle($request, $next);
            $this->assertEquals('test ok', $result);
    
            // メンテナンスでない場合、許可IPからアクセス可能であること
            $request->server = new ServerBag(['REMOTE_ADDR' => $allowIp]);
            $result = $this->target->handle($request, $next);
            $this->assertEquals('test ok', $result);
    
            //***********************************
            // メンテナンス中に変更
            $this->downCommand->run($this->input, $this->output);
    
            // メンテナンス中の場合、許可IPからのアクセスはエラーにならないこと
            $request->server = new ServerBag(['REMOTE_ADDR' => $allowIp]);
            $result = $this->target->handle($request, $next);
            $this->assertEquals('test ok', $result);
    
            // メンテナンス中の場合、許可IP以外からのアクセスは 503エラーになること
            $request->server = new ServerBag(['REMOTE_ADDR' => $otherIp]);
            try { 
                $this->target->handle($request, $next);
                $this->fail();
            } catch (Symfony\Component\HttpKernel\Exception\HttpException $e) {
                $this->assertEquals(503, $e->getStatusCode());
            }
    
            //***********************************
            // メンテナンス解除
            $this->upCommand->run($this->input, $this->output);
    
            // メンテナンス解除後、許可IP以外からアクセス可能であること
            $request->server = new ServerBag(['REMOTE_ADDR' => $otherIp]);
            $result = $this->target->handle($request, $next);
            $this->assertEquals('test ok', $result);
        }
    }

参考

  • メンテナンスモードの判定について

  • メンテナンスモードの判定は、下記のファイルに定義されている isDownForMaintenanceメソッドで行っています。

    /vendor/laravel/framework/src/Illuminate/Contracts/Foundation/Application.php

    中身は以下のようになっていて、storageパス以下の"framework/down"というファイルが存在すればメンテナンスモードと判定しています。

       public function isDownForMaintenance()
       {
           return file_exists($this->storagePath().'/framework/down');
       }