Laravelを常時SSL化する（初心者向け）

PHP・Laravelで作成したアプリをAWSのEC2へデプロイする際に、本番環境では常にHTTPS通信を行うことになると思います。
何も設定しないとHTTP通信になってしまうので、この記事ではLaravelをセキュア(=暗号化)な状態で通信するための設定を記載します。

# 環境の設定

```PHP:config/app.php
'env' => env('APP_ENV', '〇〇〇'),
```
`〇〇〇`の箇所について、開発環境の場合（ローカルで開発する際）は`local`、本番環境の場合（デプロイする際）は`production`にしてください。
ここを設定することで、開発環境か本番環境のどちらか切り替えることができます。

# URLをhttpsにする
AppServiceProvider.phpを下記のように追記してください。

```PHP:app/Providers/AppServiceProvider.php
<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\URL; //追記

class AppServiceProvider extends ServiceProvider
{
    //略

    public function boot()
    {
        // ここから追記
        if (config('app.env') === 'production') {
            URL::forceScheme('https');
        }
        // ここまで追記
    }
}
```
これで開発環境の時のURLがhttp、本番環境の時のURLがhttpsになりました。

# 本番環境において、httpでアクセスされた際にhttpsへリダイレクトさせるための処理
### middlewareの作成
次のコマンドを実行してmiddlewareを作成します。`RedirectToHttps`の箇所はご自身が作りたいクラス名を記載してください。

```
$ php artisan make:middleware RedirectToHttps
```

作成された`RedirectToHttps.php`を下記のように追記してください。

```PHP:app/Http/Middleware/RedirectToHttps.php
<?php

namespace App\Http\Middleware;

use Closure;

class RedirectToHttps
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        // ここから追記
        //このhandleメソッドで判別
        if (!$this->is_ssl() && config('app.env') === 'production') {
            return redirect('https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']);
        }
        // ここまで追記

        return $next($request);
    }

    // ここから追記
    //Webサーバー毎にキーと値で判別
    public function is_ssl()
    {
        if (isset($_SERVER['HTTPS']) === true) { // Apache
            return ($_SERVER['HTTPS'] === 'on' or $_SERVER['HTTPS'] === '1');
        }
        elseif (isset($_SERVER['SSL']) === true) { // IIS
            return ($_SERVER['SSL'] === 'on');
        }
        elseif (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) === true) { // Reverse proxy
            return (strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']) === 'https');
        }
        elseif (isset($_SERVER['HTTP_X_FORWARDED_PORT']) === true) { // Reverse proxy
            return ($_SERVER['HTTP_X_FORWARDED_PORT'] === '443');
        }
        elseif (isset($_SERVER['SERVER_PORT']) === true) {
            return ($_SERVER['SERVER_PORT'] === '443');
        }

        return false;
    }
    // ここまで追記
}

```

### Kernel.phpに登録
先程作成したRedirectToHttps.phpの内容を反映させるために、Kernel.phpの`protected $middleware`に追記します。

```PHP:app/Http/Kernel.php
    // 略

    protected $middleware = [
        \App\Http\Middleware\TrustProxies::class,
        \App\Http\Middleware\CheckForMaintenanceMode::class,
        \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
        \App\Http\Middleware\TrimStrings::class,
        \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
        \App\Http\Middleware\RedirectToHttps::class, // 追記
    ];

    // 略
```

# 補足
### 全プロキシを信用
全プロキシを信用するために、TrustProxies.phpに`*`を追記します。

```PHP:app\Http\Middleware\TrustProxies.php
protected $proxies = '*';
```
詳細は[信用するプロキシの設定](https://readouble.com/laravel/6.x/ja/requests.html?header=%25E4%25BF%25A1%25E7%2594%25A8%25E3%2581%2599%25E3%2582%258B%25E3%2583%2597%25E3%2583%25AD%25E3%2582%25AD%25E3%2582%25B7%25E3%2581%25AE%25E8%25A8%25AD%25E5%25AE%259A)を確認してください。

### CSSを反映させる
URLがhttpsになると、CSSが適用されません。
`asset`を`secure_asset`にすることで反映されます。
`app.blade.php`のheaderタグにおいて、CSSを適用している箇所をこのように書き換えると良いと思います。

```PHP:resources/views/app.blade.php
    @if(config('app.env') === 'production')
        <link rel="stylesheet" href="{{ secure_asset('css/app.css') }}">
    @else
        <link rel="stylesheet" href="{{ asset('css/app.css') }}">
    @endif
```
開発環境(http)の時は`asset`、本番環境(https)の時は`secure_asset`にすることで、CSSが通常通り反映されます。

# AWS側の設定
SSL証明書の作成、ロードバランサー、セキュリティーグループ等の設定が必要になります。
[この記事](https://qiita.com/miyuki_samitani/items/1734dc13c6b7af601bd9)がとてもわかりやすかったので、共有しておきます。
一点補足することがあります。
上記の記事ではロードバランサーの作成において、最後にターゲットの確認をして、`Health status`が`healthy`になっています。
しかし、URLをhttpからhttpsへリダイレクトさせると、`Health status`が`unhealthy`になってしまします。
`unhealthy`になっていても通常通り動くので、大した問題ではありません。
ただ、`unhealty`が気になる方は、下記の設定を行うことで`healthy`になります。
1.ターゲットグループを選択
2.Health CheckのEditをクリック
3.Advanced health check settingsのSuccess codesに302を追加し、Save changesをクリック
![Edit health check settings.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/935364/48107d89-b0cc-ade6-7eda-872ceb416d58.png)

# 参考記事
[Laravelで作ったサービスをSSL化した時にやったことと参考記事一覧](https://qiita.com/Yorinton/items/50b9c8e3102ac661f08c)
[LaravelでURLをHTTPS化させるメモ (Heroku)](https://qiita.com/ghibi/items/cb4faa2d86f5866cbfd4)
[https対応したLaravelプロジェクトでassetを使う時の注意点](https://qiita.com/e_tyubo/items/c25df0c545b1af8494c7)
[EC2上のwebサーバをSSL化対応](https://qiita.com/miyuki_samitani/items/1734dc13c6b7af601bd9)

# おわりに
最後まで読んでいただき、ありがとうございます。
間違いがあればご指摘いただけると幸いです。