Lightsail で Laravel を実行してディストリビューションを有効化するとアセットパスがオリジンパスになる問題に対処する

2022.08.11

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

いわさです。

上記を参考に、Lightsail で Laravel を実行していました。
はじめは問題なく動作していたのですが、ディストリビューションを有効化するとページのスタイルが崩れるようになってしまいました。
Laravel のアセット特有の設定が必要ということがわかったのでご紹介します。

何が起きたのか

前述の記事を参考に Lightsail 上へ Laravel を設定し適当な View と CSS を作成しました。

/opt/bitnami/apache2/htdocs/laravel/public/css/hoge.css

p {
    color: blue;
    font-weight: bold;
}

/opt/bitnami/apache2/htdocs/laravel/resources/views/hoge.blade.php

<!DOCTYPE html>
<html lang="en">
<head>
    <link rel="stylesheet" href="{{ asset('/css/hoge.css') }}" >
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    <p>hogehoge</p>
</body>
</html>

/opt/bitnami/apache2/htdocs/laravel/routes/web.php

<?php

use Illuminate\Support\Facades\Route;

/*
|--------------------------------------------------------------------------
| 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!
|
*/

Route::get('/', function () {
    return view('hoge');
});

ディストリビューションを有効化したらスタイルが適用されなくなった

Lightsail では以下のようにディストリビューションという機能を有効化することで Lightsail に統合された CloudFront を使うことが出来ます。

こちらを有効化したところ、先程まで設定できていたスタイルが適用されなくなってしまいました。

"Mixed Content: The page at 'xxx' was loaded over HTTPS, but requested an insecure stylesheet 'xxx'. This request has been blocked; the content must be served over HTTPS." が発生

ブラウザの開発者コンソールを確認してみるとエラーが発生していることが確認出来ます。

d2pzoi98pm8mbl.cloudfront.net/:4 Mixed Content: The page at 'Document' was loaded over HTTPS, but requested an insecure stylesheet 'http://ec2-54-238-33-9.ap-northeast-1.compute.amazonaws.com/css/hoge.css'. This request has been blocked; the content must be served over HTTPS.

以下のようにスタイルシートのパスがhttpになっていますね。
ただし、それ以前にホスト名がオリジンのものになっています。

対処方法

どうやら Laravel ではリバースプロキシ経由の環境でassetsecure_assetを使っても暗黙的に CDN 経由とならないそうです。
こちらの対処方法は Laravel 公式ドキュメントに記述がありました。

You can configure the asset URL host by setting the ASSET_URL variable in your .env file. This can be useful if you host your assets on an external service like Amazon S3 or another CDN:

アセットのパスを別のストレージや CDN などでホストする際には、.envファイルのASSET_URLを使うことが出来るとのこと。

やってみる

.envファイルはアプリケーションのルートディレクトに存在していますのでそちらに CloudFront のドメイン名を指定します。

/opt/bitnami/apache2/htdocs/laravel/.env

APP_NAME=Laravel
APP_ENV=local
APP_KEY=base64:13/asdsadasdsadasdasdasda/vb8U8=
APP_DEBUG=true
APP_URL=http://localhost

ASSET_URL=https://d2pzoi98pm8mbl.cloudfront.net

LOG_CHANNEL=stack
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=debug

DB_CONNECTION=mysql
:

おお、うまくいきました。
アセットの絶対パスが CDN ドメインとなっており、警告も解消されています。

さいごに

本日は、Lightsail で Laravel を実行してディストリビューションを有効化するとアセットパスがオリジンパスになる問題に対処してみました。

ドメイン関係なしに HTTP を HTTPS にしたいだけであればTrustProxiesなど他の対処方法もあるそうなのですが、今回はプロトコルを解決してもクロスオリジンになる可能性があったのでASSET_URLにてまとめて対処しました。

Web で調査したところ、assetのパスが変更出来ないのでコード側で対応する旨の記事を見つけましたが、本日時点の最新バージョンではASSET_URLを使うことが可能です。
Laravel であれば CloudFront や Lightsail に関わらずリバースプロキシ環境全般で起きそうな気もするのでassetメソッドを使う際にはこういった設定がある点も覚えておくと良さそうです。