IgnitionとCollisionで読み解くLaravelのエラー画面
概要
Laravel で開発していると、例外発生時に2種類の「エラー画面」に出会います。ブラウザに表示される詳細なエラーページと、php artisan やテスト実行時にターミナルへ出る整形済みのエラー表示です。
前者を描画しているのが Ignition、後者を担っているのが Collision です。どちらも Laravel に標準同梱されていますが、「どの経路でどちらが動くのか」「本番でなぜ出ないのか」を理解していないと、トラブルシュートで手が止まります。本記事でこの2つの役割分担と勘所を整理します。
環境
- PHP 8.1 以降(Laravel 13 を使う場合は PHP 8.3 以降が必須。現行安定版は 8.5)
- Laravel 9 / 10 / 11 / 12 / 13 系(
spatie/laravel-ignitionを前提。現行の標準同梱は 2.x 系) nunomaduro/collision(composer create-projectでrequire-devに同梱。現行は 8.x 系)
Laravel 8 以前は Ignition が facade/ignition という別パッケージでした。なお、本記事が解説する「HTTP=Ignition / コンソール=Collision」の役割分担と bootstrap/app.php の withExceptions 方式は、Laravel 11 で導入されて以降、12 / 13 でも変わっていません。セキュリティの話にも関わるため、まずバージョンを確認します。
composer show spatie/laravel-ignition composer show nunomaduro/collision
発生した問題
よくあるのは次の「見え方の食い違い」です。
- ローカルのブラウザでは詳細なスタックトレースが出るのに、本番にデプロイすると「500 Server Error」の素っ気ない画面しか出ない
php artisan testではきれいに色付きで出るのに、ログファイルは情報が少ない- エラー画面に「解決策(Solution)」が出るときと出ないときがある
これらはバグではなく、Ignition と Collision の役割分担と動作条件を把握すれば説明がつきます。
原因
エラーの流れ
捕捉されなかった例外は最終的に App\Exceptions\Handler(Laravel 11 以降では bootstrap/app.php の withExceptions)に集約されます。そこから先の「表示」が実行コンテキストで分岐します。
- HTTP リクエスト経由 → Ignition がレンダリング(
APP_DEBUG=trueのときだけ) - コンソール(artisan / テスト)経由 → Collision がレンダリング
同じ例外でも「ブラウザで見るかターミナルで見るか」で描画担当が変わります。
Ignition が本番で出ない理由
Ignition の詳細画面は config/app.php の debug(=APP_DEBUG)が true のときだけ有効です。
// config/app.php
'debug' => (bool) env('APP_DEBUG', false),
本番では APP_DEBUG=false にするのが鉄則なので、Ignition は無効化され、resources/views/errors/500.blade.php 等の汎用エラーページが表示されます。「本番で詳細が出ない」は正常な挙動です。逆に本番で Ignition の詳細画面が見えているなら、それは設定ミスであり即座に直すべき状態です。
Collision がコンソールを担当する理由
Collision は CollisionServiceProvider を通じて artisan とテストランナーに統合されます。require-dev に入っているため、本番の composer install --no-dev では読み込まれません。本番でコンソールエラーの見た目が素っ気ないのはこのためです。
対応方法
まず APP_DEBUG を確認する
php artisan tinker
>>> config('app.debug')
- ローカルで詳細が出ない →
.envがAPP_DEBUG=falseになっていないか、設定キャッシュが古くないか - 本番で詳細が出てしまう → ただちに
APP_DEBUG=falseにして設定キャッシュを張り直す
php artisan config:clear php artisan config:cache
config:cache を実行すると .env の変更が反映されなくなります。.env を変えたら必ずキャッシュを張り直してください。
Ignition の挙動を絞り込む
設定ファイルを公開すると、挙動を細かく確認・制御できます。
php artisan vendor:publish --provider="Spatie\LaravelIgnition\IgnitionServiceProvider"
Solution(修正提案や実行可能アクション)は開発を速くしますが、本番では無効であるべきです。APP_DEBUG=false であれば Ignition 自体が動かないため、ここが担保の要になります。
実装例
HTTP とコンソールで表示が分岐することを、意図的に例外を投げて確認します。
// routes/web.php
Route::get('/boom', function () {
throw new \RuntimeException('HTTP 経由の例外です');
});
// app/Console/Commands/BoomCommand.php
namespace App\Console\Commands;
use Illuminate\Console\Command;
class BoomCommand extends Command
{
protected $signature = 'demo:boom';
protected $description = 'コンソール経由で例外を投げる';
public function handle(): int
{
throw new \RuntimeException('コンソール経由の例外です');
}
}
/boomにブラウザでアクセス(APP_DEBUG=true)→ Ignition の詳細画面php artisan demo:boom→ Collision の整形出力
同じ RuntimeException でも描画担当が違うことが体感できます。例外ハンドラ側でのカスタマイズも可能です。
// Laravel 11 以降: bootstrap/app.php
->withExceptions(function (Illuminate\Foundation\Configuration\Exceptions $exceptions) {
$exceptions->report(function (\Throwable $e) {
// 独自ログ送信など
});
})
確認方法
.envをAPP_DEBUG=trueにしてphp artisan config:clear/boomにアクセスし、スタックトレース付きの Ignition 画面が出ることを確認.envをAPP_DEBUG=falseにしてphp artisan config:clear、再度/boomで汎用 500 ページに変わることを確認php artisan demo:boomを実行し、Collision の整形出力を確認
この4ステップで「誰が・どの条件で描画しているか」が切り分けられます。
注意点
最大の注意点はセキュリティです。過去に Ignition(当時の facade/ignition)には、デバッグモードが有効な公開環境を狙った深刻な脆弱性(CVE-2021-3129、リモートコード実行につながるもの)が報告され、修正版がリリースされました。教訓はシンプルです。
- 本番では
APP_DEBUG=falseを徹底する(Ignition の詳細画面・Solution 実行を露出させない) - Ignition / Laravel 本体を最新に保つ
- デバッグツールを
require-devのまま本番へ混入させない(composer install --no-dev --optimize-autoloader)
便利なエラー画面は、裏を返せばアプリ内部を雄弁に語る画面でもあります。開発を速くする機能ほど、公開環境での露出に神経を使う必要があります。
まとめ
- Laravel のエラー表示は HTTP=Ignition / コンソール=Collision で役割が分かれている
- Ignition の詳細画面は
APP_DEBUG=trueのときだけ動く。本番で素っ気ないのは正常 - Collision は
require-dev同梱で、artisan・テストの出力を整形する - トラブルシュートはまず
APP_DEBUGと設定キャッシュの確認から - 過去の RCE 事例を踏まえ、本番でのデバッグモード無効化とバージョン更新を徹底する
「なぜこの画面が出ているのか」を説明できると、エラー対応の初動が一段速くなります。
