このブログをLaravel 13 + Filament 5で作り直し、2026年11月13日にお名前.com レンタルサーバーRSで切り替えました。

10時半ごろ、メンテナンスモードのまま管理画面のログインを開くと、見出しと入力欄が並んだだけの素のHTMLが表示されました。Filamentのスタイルがひとつも当たっていません。トップページを開いてみると、こちらもデザインが崩れていました。

CSSが当たっていないトップページ

最初に疑ったこと

最初は、CSSのファイルそのものが読み込めていないのだと思いました。

ところがClaude Codeにシンボリックリンクをたどってもらうと、CSSもJSも新しいリリースのファイルにつながっていました。CSSのファイルをhttpsで直接開くと、問題なく表示されます。ファイル自体は問題なさそうでした。

HTMLの中のURLを数えてみると、ログイン画面で18個、トップページで53個が http:// で始まっていました。

curl -s https://example.com/ | grep -o 'http://[^"]*' | wc -l

原因はhttpsの判定

お名前.com RSでは、HTTPSを受けるのは前段のプロキシで、PHPにはhttpとして渡されます。Laravelはリクエストを見てURLを作るので、asset() も @vite もFilamentやLivewireのURLも、すべて http:// になっていました。

公開前のチェックではcanonicalがhttpsになっているかしか見ていませんでした。canonicalは APP_URL から作っているので、そこだけはhttpsのままで、気付けませんでした。ローカルもhttpで確認していたので、Mixed Contentは起きていませんでした。

作り直す前の旧サイトのコードを見ると、TrustProxiesと URL::forceScheme('https') が入っていました。新しいサイトに移すのを忘れていたわけです。

旧サイトと同じ設定で見つかった別の問題

まず旧サイトと同じく、すべてのプロキシとすべての X-Forwarded-* ヘッダーを信頼する設定にしました。

// bootstrap/app.php(最初に入れた設定)
->withMiddleware(function (Middleware $middleware): void {
    $middleware->trustProxies(at: '*');
})

CSSは直りました。ただ、念のため前段が実際にどのヘッダーを送っているのかを確かめたところ、こうなっていました。

ヘッダーなど 結果
X-Forwarded-Proto https
X-Forwarded-Port 443
X-Forwarded-For 来ない
REMOTE_ADDR 訪問者のIP

前段は X-Forwarded-For を付けていません。それなのに信頼する設定にしていると、訪問者が自分で送った値がそのまま $request->ip() になります。偽のヘッダーを付けて試すと、セッションに保存されたIPが本当に偽の値になりました。

curl -s -H 'X-Forwarded-For: 198.51.100.23' https://example.com/ > /dev/null

問い合わせフォームには「同じIPから10分に3回まで」という制限を入れていたので、これではヘッダーを変えるだけで制限を抜けられます。旧サイトも同じ設定だったので、ずっと同じ状態だったことになります。

この設定では X-Forwarded-Host も信頼の対象になるので、訪問者が送ったホスト名がURLに使われるおそれもあります。

最終的な設定

httpsの判定に必要な Proto と Port だけを信頼するようにしました。

// bootstrap/app.php
use Illuminate\Foundation\Configuration\Middleware;
use Illuminate\Http\Request;

// ...

->withMiddleware(function (Middleware $middleware): void {
    $middleware->trustProxies(
        at: '*',
        headers: Request::HEADER_X_FORWARDED_PROTO
            | Request::HEADER_X_FORWARDED_PORT,
    );
})

前段がProtoを送らなくなったときの保険として、本番ではURLを常にhttpsで作ります。

// app/Providers/AppServiceProvider.php
use Illuminate\Support\Facades\URL;

public function boot(): void
{
    if ($this->app->isProduction()) {
        URL::forceScheme('https');
    }
}

もう一度、偽の X-Forwarded-For と X-Forwarded-Host: evil.example を付けて試し、IPは本物のまま、HTMLにも偽のホストが出ないことを確認しました。

前段の動きはサーバーによって違うので、次からは trustProxies を書く前に、実際に届いているヘッダーを先に見るようにします。今回は特定のクエリが付いたときだけヘッダーをファイルに書き出す1行を一時的に入れ、確認後すぐに消しました。

公開まで

原因の調査と確認はClaude Codeに手伝ってもらいながら進めました。「公開前に15分で原因が分からなければ旧サイトに戻す」と決めていましたが、原因は時間内に分かり、11時2分に公開できました。

再発防止として、公開後に流すスモークテストに「HTMLの src・href に http:// がないこと」を加えました。修正前の本番サーバーに流すと、ちゃんと失敗しました。