Laravel5.5製のWebアプリケーションをhttps化させた時に遭遇した問題とその解決方法について書きます。
読み込みファイルのパスのプロトコルが変わらない問題
Laravelにはviewでcssやjsを読み込む際にフルパスを記述するためのメソッドが用意されています。
たとえばcssを読み込みたい時は
<link href="{{ asset('css/your_app.css') }}" rel="stylesheet">
jsを読み込みたい時は
<script src="{{ asset('js/your_app.js') }}"></script>
と書くことができます。
これらは実際にローカルでページにアクセスすると
# css
http://localhost/css/your_app.css
# js
http://localhost/js/your_app.js
のように展開されます。
headerの情報を元に参照hostを判断して動的に生成してくれます。
しかし、なぜかプロトコルのところはアクセス元の情報に沿わずにhttpとして展開されてしまいます。
https://your_domain
にアクセスしたら
# css
http://your_domain/css/your_app.css
# js
http://your_domain/js/your_app.js
と展開されます。
これだとcssとjsのロードエラーがフロントで発生するため、何かしら回避策が必要です。
回避方法
そこで、https通信のときだけassetで返されるURLをhttpsにすることにしました。
まずhttpsかどうかの判定にはリクエストオブジェクトから呼び出せるisSecure
というメソッドを使って判定しました。
これは単純にアクセス元のプロトコルだけでなく、headerのX_FORWARDED_PROTO
も見てくれるので、AWSでロードバランサーを介してhttps化している場合でも対応が可能です。
ただし、ロードバランサー経由の場合は追加で
protected $proxies = '**';
と記述する必要があります。
Laravelはデフォルトではプロキシー経由のリクエストにおけるhttpsを許容していないらしく、TrustProxies.phpに追記する必要があります。AWSのロードバランサーはipアドレスが動的に変わるためワイルドカードを指定します。
参考: https://readouble.com/laravel/5.5/ja/requests.html#configuring-trusted-proxies
そして、URL生成でhttpsを強制するための方法としてforceScheme
というメソッドが用意されているので、これを使いました。
https://laravel.com/api/5.5/Illuminate/Routing/UrlGenerator.html#method_forceScheme
AppServiceProviderのboot()の中に下記の記述をすることで対応できます。
public function boot()
{
if (request()->isSecure()) {
\URL::forceScheme('https');
}
}
これでhttpでもhttpsでも大丈夫になりました。
最後に
調べていると本番環境は常にhttpsにするって方法がちらほら出てきましたが、ステージング環境とか必要箇所が増えるたびに環境変数の対応箇所が増えてきてしまうので、単純に通信のプロトコルをみて判断できる実装のほうが良いのではないかなと思いました。
また、こういうエラーはローカルでは検証しづらく、httpsな環境にあげたときにしか気づけ無いので、httpsなステージング環境は必要ですね。