Laravel で「CORS error」と表示された真犯人を突き止めるまで ―― “dump がヘッダーを確定させていた” という落とし穴と、その対処法

症状 ―― CORS 設定を直しても消えない「CORS error」

  • フロント(Next.js)から POST /api/entry を呼ぶと DevTools に “CORS error” と表示される。
  • ネットワークタブには Status 200 / Size 0 B / Content-Type text/html が並ぶ(JSON を返すはずが HTML になっている)。
  • config/cors.php を編集したり、nginx で add_header を加えても改善しない。

> ここで「本当に CORS が原因なのか?」を疑うのがポイントです。


調査手順と気付き

1. curl でエンドポイントを叩く
まず curl -i -X POST http://localhost/api/entry を実行してみました。すると、ブラウザでは 200 ステータスが返ってきていたにもかかわらず、curl では 500 Internal Server Error が返ってきました。これにより、問題は CORS ではなくサーバーエラーであることが判明しました。

2. ブラウザのネットワークタブを再確認
ブラウザのネットワークタブを見ると、レスポンスのコンテンツタイプが text/html になっていることに気付きました。本来 API レスポンスであれば application/json のはずです。これは 何かが先に HTML を出力している可能性 を強く示唆していました。

3. Postman で同じリクエストを送信
Postman で同じリクエストを送信したところ、レスポンスボディには <pre class="xdebug-var-dump"...> で始まる HTML(dump の内容)がそのまま返ってきました。これは dump の出力がそのまま HTTP レスポンスになっていることを示しています。

4. storage/logs/laravel.log を tail
storage/logs/laravel.log を tail したところ、外部 API 呼び出し前後でログが途切れていることがわかりました。これは 途中で例外または予期せぬ出力が挟まっている可能性 を示しています。

5. Xdebug でブレークポイントを設定
サービス層のメソッド中で dump($xml) が実行されている箇所を発見しました。これは dump が HTML を即時出力しヘッダーを確定させていることを示しています。


原因 ―― dump/dd/echo がヘッダーを先に送信していた

dump() はデバッグ用関数で、呼ばれた瞬間にブラウザへ HTML を送り出します。レスポンスヘッダーが Content-Type: text/html で確定してしまうため、その後に付与されるはずだった Access-Control-Allow-Origin などの CORS ヘッダーが入らなくなります。結果、ブラウザからは 「CORS エラー」としか見えません


修正方法 ―― サービス層の dumpLog::debug に差し替える

// app/Services/HogeSyncService.php
class HogeSyncService
{
    public function sync(array $payload): array
    {
        $xml = $this->buildXml($payload);

        // ✗ NG: dump するとヘッダーが確定してしまう
        // dump($xml);

        // 〇 OK: Log に残せばブラウザへは何も送られない
        Log::debug('Hoge XML payload', ['xml' => $xml]);

        Http::post(config('hoge.endpoint'), $xml);

        return ['status' => 'ok'];
    }
}


改善ポイントとベストプラクティス

本番環境のコードには dumpddecho などのデバッグ用の出力を残さないようにしましょう。これらの関数はヘッダーが確定する前に呼び出されると、意図しないレスポンスヘッダーが送信されてしまう可能性があります。代わりに、ログ出力を使用するか、デバッグが完了したら完全に削除することをお勧めします。


まとめ

1. ブラウザが出す “CORS error” は 必ずしも CORS 設定の誤りを示すわけではない
2. Laravel/PHP では ヘッダー確定前に dump/echo を呼ぶ と CORS ヘッダーが付けられず、同じエラー表示になる。
3. デバッグ出力は Log クラスへ切り替え(もしくは削除)、レスポンスは常に JSON で返すようにすれば解決。

> まずは curl と Postman とログを確認する。それだけで「CORS か、それ以外か」を簡単に切り分けられます。

よかったらシェアしてね!
  • URLをコピーしました!

この記事を書いた人

はじめまして、「知識を愛する者」愛知郎です。

五反田でエンジニアとして活動してます。

こちらでは、私が最近学んだこと発信しています。

目次