nginxやロードバランサーなどを使ってWebサーバーにアクセスさせた際に困るのが、一部の情報が本体のWebサーバーまで到達しない点。
特に、クライアントがHTTPでアクセスしているのか、HTTPSでアクセスしているのか、などの情報はそのままの状態で取得するのは困難です。
こういった環境と、HTTPかHTTPSどちらで接続されているのかを判定したうえでDOMのURLを書き換えるタイプの機能を持つフレームワークやプログラムとの相性は特に最悪で、Webサーバーに対してHTTPでアクセスされた時点で”クライアントからHTTPでアクセスされている”と判定して、すべてのアセットのアドレスをhttp://から始まるURLに置き換えて返してしまい、結果jsやcssが読み込めずに表示崩れや不具合になってしまうことがあります。
最近人気のLaravelもこの機構を持っており、そのままの状態でnginxやLBを間に置いてしまうと、httpsでアクセスしたときにページが正常に表示されない問題が発生してしまいます。
幸いにも、HTTPSかどうかの部分についてnginxやロードバランサーが代わりに取得し、その情報をヘッダーに含めて投げてくれる場合が多いので、今回はLaravel上でこのヘッダ情報を使って簡単にHTTP/HTTPSを判定できるようにしてみました。
Laravelプロジェクトフォルダ内の
routes/web.php
の最初あたりに、次のコードを追加します。
<?php // 2018/10/26追記: 一部修正しました。 /* |-------------------------------------------------------------------------- | Web Routes |-------------------------------------------------------------------------- | | Here is where you can register web routes for your application. These | routes are loaded by the RouteServiceProvider within a group which | contains the "web" middleware group. Now create something great! | */ //ここから //リバースプロキシ経由のHTTP/HTTPS判定 if(array_key_exists('HTTP_X_FOWARDED_PROTO',$_SERVER) === true){ //"HTTP_X_FOWARDED_PROTO"ヘッダーが存在していたら if(strtoupper($_SERVER["HTTP_X_FOWARDED_PROTO"]) == "HTTP"){ //strtoupper("HTTP_X_FOWARDED_PROTO")の中身が"HTTP"だったら URL::forceScheme('http'); //すべてのURLをhttpに書き換える } else { URL::forceScheme('https'); //すべてのURLをhttpsに書き換える } } //ここまで
$_SERVERはPHPにおいて、サーバー情報および実行時の環境情報がすべて格納されるスーパーグローバル変数です。
この中に、リクエストヘッダの中身もすべて格納されるので、その中からnginxやAmazon ALBやClassic Load BalancerがHTTP/HTTPSの情報を格納する”X_FOWARDED_PROTO”ヘッダーを参照して、どちらでアクセスされたかを判定しています。
ifを敢えてHTTPかどうかで判定している理由は万が一、”$_SERVER[“HTTP_X_FOWARDED_PROTO”]”にhttpでもhttpsでもない不正な値が入っていた場合も、とりあえずhttps://で吐き出すようにするためです。
こんな形で処理を加えてやれば、Laravelでもnginxやロードバランサーを使用していても正常にHTTPとHTTPSの切り替えが行えるようになると思います。
少しでも参考になりましたらしたら幸いです。