Laravel『Response Too Large』の原因と対処法
- 作成日 2025.12.15
- その他
Laravelで「Response Too Large」が出る場合、アプリ(Laravel)単体の例外というより、APIゲートウェイ・リバースプロキシ・CDN・ブラウザ・クライアントSDKなど“途中のレイヤ”が「レスポンスが大きすぎる」と判断して落としているケースが多い。巨大なJSON(大量レコード、巨大なネスト、Base64画像の直埋め)、バイナリをJSONで返している、ログやデバッグ情報を混ぜている、無限ループや循環参照で異常に膨らんだレスポンスを生成している、といった原因が典型。まず「どこが」制限しているのか(Laravel / PHP / Nginx / CDN / API Gateway / クライアント)を特定し、サイズを減らすか、配信方式(ダウンロード、ストリーミング、ページング)を変える。
- 1. エラーの出方(メッセージ例)と発生条件
- 2. まず「どのレイヤで」大きすぎると言われているかを特定する
- 3. 原因1: 全件取得や巨大配列でJSONが肥大化している
- 4. 原因2: リレーションのネスト(with の付けすぎ)で膨らむ
- 5. 原因3: Base64画像/ファイルをJSONに直埋めしている
- 6. 原因4: 循環参照や意図しない巨大構造を返している
- 7. 原因5: ヘッダ(Cookie/Set-Cookie)が大きすぎる
- 8. 原因6: ログ/デバッグ出力が混入してレスポンスが巨大化
- 9. 対処: ページング / チャンク / ストリーミングで“返し方”を変える
- 10. 対処: 圧縮(gzip/brotli)を有効化して転送量を減らす
- 11. 対処: どうしても制限値に当たる場合の設定見直し(どこが制限しているか次第)
- 12. チェックリスト(上から順に確認する)
エラーの出方(メッセージ例)と発生条件
「Response Too Large」は環境によって表現が変わる。以下はよくある例。
# 例: API Gateway / Proxy / SDK 側のメッセージ
ResponseTooLargeException
Response too large
Payload Too Large
413 Payload Too Large
upstream sent too big header
client intended to send too large body発生条件の典型:
・大量データを一括で返す(全件取得、CSVをJSONに詰める、巨大な配列)
・画像/ファイルをBase64でJSONレスポンスに埋め込む
・Eager load しすぎ + リレーションのネストが深すぎて JSON が膨張する
・循環参照(親→子→親)を含む構造をそのまま返して無限に膨らむ/例外になる
・CookieやHeaderが大きすぎて、ヘッダ制限で落ちる(“body”ではなく“header”が原因)
・デバッグツール(Debugbar、Xdebug、dump())の出力が混入してレスポンスが肥大化
まず「どのレイヤで」大きすぎると言われているかを特定する
同じ現象でも、制限している場所が違えば対処も違う。切り分けの軸は「HTTPステータス」と「ログ」。
・413 が返っている → だいたいリバプロ/ゲートウェイ側の制限
・500 でアプリ側ログに巨大配列生成の痕跡 → Laravel側で巨大レスポンス生成
・“upstream sent too big header” → ヘッダ制限(Nginx等)
・クライアントSDKが「Response Too Large」と言う → クライアント側の受信制限の場合もある
確認用コマンド例。
# ステータスとヘッダを確認
curl -i https://example.com/api/items
# レスポンスサイズを計測(概算)
curl -s https://example.com/api/items | wc -c
# 圧縮後サイズ(gzip)も確認
curl -sH 'Accept-Encoding: gzip' https://example.com/api/items --compressed | wc -c
原因1: 全件取得や巨大配列でJSONが肥大化している
例:Eloquentで全件を取得して、そのまま返す。
// NG: 全件をメモリに載せて巨大JSONを返す
Route::get('/users', function () {
return response()->json(\App\Models\User::all());
});
[/code]対処は「ページング」「検索条件」「必要カラムだけ」を徹底する。
// OK: ページング + 必要カラムだけ
Route::get('/users', function () {
return \App\Models\User::query()
->select(['id', 'name', 'email'])
->orderBy('id')
->paginate(50);
});大量データを返すAPIは、limit/offset か cursor pagination を基本にして、デフォルトで小さく返す。
原因2: リレーションのネスト(with の付けすぎ)で膨らむ
Eager load を増やすほどレスポンスは肥大化する。さらにネストが深いと一気に増える。
// NG: ネストが深すぎる
$orders = Order::with(['user', 'items.product', 'items.product.category', 'payments', 'shipments'])->get();
return response()->json($orders);対処:
・必要な場面だけ with を付ける
・必要なカラムだけ選ぶ(with の中でも select を使う)
・Resource(JsonResource)で出力を制御する
Resource例。
// app/Http/Resources/OrderResource.php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class OrderResource extends JsonResource
{
public function toArray($request)
{
return [
'id' => $this->id,
'total' => $this->total,
'user' => [
'id' => $this->user_id,
'name' => optional($this->user)->name,
],
'items' => $this->items->map(fn($i) => [
'id' => $i->id,
'product_id' => $i->product_id,
'qty' => $i->qty,
]),
];
}
}呼び出し側。
return OrderResource::collection(
Order::with(['user:id,name', 'items:id,order_id,product_id,qty'])
->paginate(30)
);原因3: Base64画像/ファイルをJSONに直埋めしている
画像やPDFをBase64にしてレスポンスに載せると、サイズが爆増しやすい(Base64は約1.33倍)。
// NG: 画像をBase64で直埋め
return response()->json([
'image' => base64_encode(Storage::get($path)),
]);対処:
・署名付きURLを返して、ファイルは別エンドポイントでダウンロードさせる
・S3などに置いてURLだけ返す
・どうしてもAPIで返すなら streamDownload / download を使う
署名付きURL例。
$url = Storage::disk('s3')->temporaryUrl(
$path,
now()->addMinutes(10)
);
return response()->json(['url' => $url]);原因4: 循環参照や意図しない巨大構造を返している
Modelをそのままjson化すると、リレーションやアクセサが連鎖して想定以上に膨らむことがある。親子関係などで循環参照があると危険。
対処:
・Resourceで出力を固定する
・不要なリレーションを隠す($hidden)
・アクセサで巨大データを生成していないか確認する
$hidden例。
// app/Models/User.php
protected $hidden = [
'password',
'remember_token',
'large_blob_column',
];原因5: ヘッダ(Cookie/Set-Cookie)が大きすぎる
「レスポンスボディ」ではなく「レスポンスヘッダ」が大きすぎて落ちるケースもある。Nginxなら “upstream sent too big header” が典型。
発生条件:
・Cookieに巨大データを詰めている
・セッション/認証の仕組みでSet-Cookieが肥大化している
・ヘッダに長いトークンやデバッグ情報を載せている
対処:
・Cookieにデータを詰めない(IDだけにする)
・セッションドライバを file/redis/database にして中身はサーバ側へ
・レスポンスヘッダのサイズ上限(Nginx等)を見直す(必要なら)
LaravelのセッションをRedisに寄せる例。
# .env
SESSION_DRIVER=redis
CACHE_DRIVER=redis原因6: ログ/デバッグ出力が混入してレスポンスが巨大化
dump(), dd(), Debugbar、もしくは例外時にHTMLエラーページが返って“見た目はJSONのつもり”が実際は巨大HTMLになっていることがある。
対処:
・本番では APP_DEBUG=false を徹底
・APIは例外ハンドリングでJSONに統一
・ログはレスポンスに混ぜず、サーバ側ログへ
API例外をJSON化する方向性(概念)。
// app/Exceptions/Handler.php のrender内などで、APIならJSONに寄せる
if ($request->expectsJson()) {
return response()->json([
'message' => $e->getMessage(),
], 500);
}対処: ページング / チャンク / ストリーミングで“返し方”を変える
大量データを返す必要がある場合、「レスポンスを小さくする」のではなく「分割して返す」「ダウンロードにする」「非同期にする」へ切り替える。
・一覧API → paginate / cursorPaginate
・集計 → DB側で集計して返す(生データを返さない)
・エクスポート → CSV生成してファイルとして返す(ストリーミング)
CSVをストリーミングする例。
use Symfony\Component\HttpFoundation\StreamedResponse;
Route::get('/export/users', function () {
$response = new StreamedResponse(function () {
$out = fopen('php://output', 'w');
fputcsv($out, ['id', 'name', 'email']);
\App\Models\User::query()
->select(['id', 'name', 'email'])
->orderBy('id')
->chunk(1000, function ($users) use ($out) {
foreach ($users as $u) {
fputcsv($out, [$u->id, $u->name, $u->email]);
}
});
fclose($out);
});
$response->headers->set('Content-Type', 'text/csv');
$response->headers->set('Content-Disposition', 'attachment; filename="users.csv"');
return $response;
});対処: 圧縮(gzip/brotli)を有効化して転送量を減らす
JSONは圧縮が効きやすいので、転送量が原因で落ちている場合は改善することがある。ただし“サイズ制限”が圧縮前なのか圧縮後なのかはレイヤによって違う。
確認として、Accept-Encoding を付けたときのサイズを測ると傾向が分かる。
curl -sH 'Accept-Encoding: gzip' https://example.com/api/items --compressed | wc -c
圧縮は「根本の巨大レスポンス」を隠すだけになることもあるので、まずは不要データ削減が優先。
対処: どうしても制限値に当たる場合の設定見直し(どこが制限しているか次第)
503/413/SDK例外など、制限をかけている場所が特定できたら、そのレイヤの上限を見直す選択肢もある。
代表例:
・Nginx: client_max_body_size(主にリクエスト)/ proxy_buffer_size(ヘッダ・バッファ)
・API Gateway / CDN: レスポンス最大サイズ
・PHP-FPM/アプリ: メモリ制限で巨大配列生成が落ちる(memory_limit)
Nginx側の例(概念)。
# request body が大きい場合(アップロード系)
client_max_body_size 20m;
# upstream response header が大きい場合(Cookie肥大など)
proxy_buffer_size 16k;
proxy_buffers 8 16k;設定変更は“最後の手段”に寄せ、まずレスポンス設計を小さくする方が安定する。
チェックリスト(上から順に確認する)
1) 実際に返っているHTTPステータスは何か(413/500/503など)を確認したか
2) 「どのレイヤ」が大きすぎると言っているか(Laravel/PHP/Nginx/CDN/API Gateway/クライアント)をログで特定したか
3) レスポンスサイズ(curl | wc -c)を測って、どのエンドポイントが巨大化しているか把握したか
4) 一覧系APIで all()/get() の全件返しをしていないか(paginate/cursorPaginateにできないか)
5) with のネストが深すぎないか、Resourceで必要フィールドだけ返しているか
6) Base64をJSONに埋め込んでいないか(URL返却やダウンロードへ切り替えられないか)
7) Cookie/ヘッダが肥大化していないか(upstream sent too big header の兆候がないか)
8) APP_DEBUG=true や dump()/dd() の混入でレスポンスが膨らんでいないか
9) 大量データはストリーミング/非同期/ファイル配布に切り替える設計にできないか
-
前の記事
Laravel『Class Not Found』エラーの原因と対処法 2025.12.14
-
次の記事
Laravel『Call to a Member Function on String』の原因と対処法 2025.12.16
コメントを書く