LaravelでHTTPSに対応するための設定とコーディング

Laravel

「ELB配下にLaravelをインストールしたらCSSや画像が見れない」「secure_url関数って使う必要があるの?」

LaravelのサイトをHTTPS対応しようとして困ったことはありませんか?

LaravelでのURL生成は、動作環境に応じて柔軟に切り替わるようになっているので、仕組みを理解しないと解決に時間がかかるかもしれませんね。

この記事では、LaravelサイトをHTTPS化するのに必要となる関数や設定を詳しく紹介します。

リンクやリソースへのURLをhttpsにする

まず、リンクのURLや、画像・CSS・JSの参照パスがhttps:// になるようにします。

Bladeテンプレートでは、url()関数やasset()関数、route()関数などを使い、URLを指定しているものとします。

手前にロードバランサがない環境のとき

ELBなどSSLアクセラレータがなく、サーバー1台構成の場合は、url()関数やassets()関数をそのまま使えます。

<a href="{{ route('mypage') }}">Myページ</a>
<link rel="stylesheet" href="{{ asset('css/app.css') }}">
<img src="{{ url('storage/foo.img') }}">

リクエストがHTTPSのとき、assets()関数が生成するURLは自動的にhttps://になります。

<a href="https://example.com/mypage/">TOP</a>
<link rel="stylesheet" href="https://example.com/asset/css/app.css">
<img src="https://example.com/storage/foo.img">

ELB配下のLaravel環境のとき

ELB環境では、App\Http\Middleware\TrustedProxiesミドルウェアを次のように書き換えます。

class TrustProxies extends Middleware
{
    /**
     * The trusted proxies for this application.
     *
     * @var array|string|null
     */
    protected $proxies = '*';

    /**
     * The headers that should be used to detect proxies.
     *
     * @var int
     */
    protected $headers = Request::HEADER_X_FORWARDED_AWS_ELB;
}

こうすると、ELB経由のHTTPSリクエストのとき、url()関数やassets()関数、route()関数が生成するURLは自動的にhttps://になります。

Apache配下のLaravel環境のとき

Dockerのローカル開発環境で、Apache(リバースプロキシ)配下にLaravelを構築しているようなケースです。

まず、App\Http\Middleware\TrustedProxiesミドルウェアを次のように書き換えます。

class TrustProxies extends Middleware
{
    /**
     * The trusted proxies for this application.
     *
     * @var array|string|null
     */
    protected $proxies = '*';

    /**
     * The headers that should be used to detect proxies.
     *
     * @var int
     */
    protected $headers = Request::HEADER_X_FORWARDED_FOR | Request::HEADER_X_FORWARDED_HOST | Request::HEADER_X_FORWARDED_PORT | Request::HEADER_X_FORWARDED_PROTO;
}

ProxyしているApacheの設定で、X-Forwarded-Protoヘッダを送信します。

例えば、/etc/httpd/conf.d/ssl.conf で、RequestHeaderディレクティブを使い、X-Forwarded-Protoヘッダにhttpsを設定します。

<VirtualHost _default_:443>
        :
        :
    ProxyPass / http://127.0.0.1/
    ProxyPreserveHost On
    <Proxy *>
        RequestHeader set X-Forwarded-Proto https
    </Proxy>
</VirtualHost>

こうすると、SSLアクセラレータ代わりのApache経由でHTTPSリクエストがやってきたとき、url()関数やassets()関数、route()関数が生成するURLがhttps://になります。

ちなみに、ELB は X-Forwarded-Protoヘッダを自動的に送信するので、この設定は不要でしたが、ApacheはX-Forwarded-Protoヘッダを送信しないので、この設定が必要です。

HTTP ヘッダーと Application Load Balancer – Elastic Load Balancing

環境に依存せずURLをhttps化できる方法

方法① secure_url()関数やsecure_asset()関数を使う

これまで説明したurl()関数やasset()関数は、環境によって動作が変化するので、分かりにくい面もあります。

代わりに、secure_url()関数やsecure_asset()関数を使うと、環境によらずhttps://で始まるURLを生成できます。

変更前:

<link rel="stylesheet" href="{{ asset('css/app.css') }}">
<img src="{{ url('storage/foo.img') }}">

変更後:

<link rel="stylesheet" href="{{ secure_asset('css/app.css') }}">
<img src="{{ secure_url('storage/foo.img') }}">

helpers.phpのsecure_assets()関数の実装を見ると、secure変数をtrue固定で渡しています。

    /**
     * Generate an asset path for the application.
     *
     * @param  string  $path
     * @return string
     */
    function secure_asset($path)
    {
        return asset($path, true);
    }

環境によって動作が変わることもなく、確実な方法です。

secure_url()やsecure_asset() 関数を使うと、生成されるURLは常にhttps://になります。「ローカル開発環境だけhttp:// にする」ということはできなくなるので、注意してください。

方法② UrlGeneratorをカスタマイズする

secure_url()関数を使わない方法としては、UrlGeneratorクラスをカスタマイズし、強制HTTPS化する方法があります。

AppServiceProviderクラスで、UrlGeneratorのforceScheme関数を呼び出します。

use Illuminate\Routing\UrlGenerator;

class AppServiceProvider extends ServiceProvider
    public function boot(UrlGenerator $url)
    {
        $url->forceScheme('https');
    }
}

セッションCookieにsecure属性を付与する

.envファイルで、SESSION_SECURE_COOKIE を true に設定します。

SESSION_SECURE_COOKIE=true

SESSION_SECURE_COOKIEが未設定の時は、Cookieにsecure属性が設定されません。

例えば、ログイン時に送信されるSet-Cookieヘッダは、以下のようになっています。

SESSION_SECURE_COOKIEをtrueに設定すると、次のようにsecure属性が設定されるようになります。

.envのAPP_URLはHTTPS対応に関係なし

.envファイルにアプリケーションのURLを指定するAPP_URLがありますが、HTTPS判定には使われていません。

LaravelのHTTPS対応には様々な方法があるので、利用環境に応じて、使い分けてみてください。