状況
深夜2時。ユーザーから「何もアップロードできない」と報告が入ります。真っ白なページ、またはブラウザの曖昧なエラー。/var/log/nginx/error.logを確認すると:
2025/03/16 02:13:44 [error] 1234#1234: *42 client intended to send too large body: 12582912 bytes, client: 203.0.113.55, server: example.com, request: "POST /upload HTTP/1.1", host: "example.com"
12,582,912バイト — つまり12MBです。ブラウザにはシンプルにこう表示されます:
413 Request Entity Too Large
Nginxはアプリケーションに届く前にリクエストを遮断しました。修正には2つのレイヤーがあります — 2つ目を見落とすと問題がすぐに再発します。
原因
Nginxは受信リクエストボディのサイズに上限を設けています。デフォルトは1MB(client_max_body_size 1m)です。ファイルアップロード、JSONペイロード、フォーム送信など、この制限を超えるPOSTリクエストはすべて即座に413で遮断されます。
これはNginxレベルのゲートであり、アプリケーション独自のアップロード制限とは完全に別物です。PHPのupload_max_filesize、Node.jsのボディパーサー設定、S3の署名付きURL設定 — Nginxが先にリクエストをブロックすれば、これらは何の意味も持ちません。アプリが100MBのファイルを受け付けるよう設定されていても、明示的に指定しない限りNginxは1MBを超えるものをすべて遮断します。
即時修正(稼働中のサーバー)
まず、実際に有効な設定を確認します:
# 有効な設定を確認する
nginx -T | grep -n client_max_body_size
# またはすべてのnginx設定ファイルを検索する
grep -r client_max_body_size /etc/nginx/
出力がなければデフォルト(1MB)が適用されています — このディレクティブは一度も明示的に設定されていません。サーバーブロックに追加または更新します:
sudo nano /etc/nginx/sites-available/your-site.conf
server {}または特定のlocation {}ブロック内に追加します:
server {
listen 80;
server_name example.com;
# 最大20MBのアップロードを許可する
client_max_body_size 20m;
location / {
proxy_pass http://localhost:3000;
}
}
テストしてリロード — ダウンタイムは不要です:
sudo nginx -t && sudo systemctl reload nginx
恒久的な修正 — 適切なスコープで設定する
client_max_body_sizeをどこに記述するかが重要です。Nginxは最も内側のマッチするブロックを適用するため、単一のエンドポイントまで細かく制御できます:
オプション1:グローバル(すべてのバーチャルホスト)
/etc/nginx/nginx.confのhttp {}ブロック内:
http {
client_max_body_size 20m;
...
}
オプション2:バーチャルホストごと
特定のserver {}ブロック内 — グローバル値を上書きします:
server {
server_name api.example.com;
client_max_body_size 50m;
...
}
オプション3:ロケーションごと(最も精密)
特定のエンドポイントだけ上限を上げたい場合はここに設定します。それ以外はデフォルトのままです:
server {
server_name example.com;
client_max_body_size 1m; # それ以外のすべてのデフォルト
location /api/upload {
client_max_body_size 100m; # このエンドポイントのみ
proxy_pass http://localhost:3000;
}
}
制限を完全に無効化する(非推奨)
0に設定するとチェックが完全に無効になります。内部または信頼できるネットワークのみで使用してください:
client_max_body_size 0;
PHP-FPMを使用している場合
NginxはリクエストをPHPに通すようになりましたが、PHPが拒否する可能性があります。以下の2つの設定を確認してください:
# php.iniの場所を確認する
php --ini | grep "Loaded Configuration"
# 編集する
sudo nano /etc/php/8.1/fpm/php.ini
両方の値がNginxの制限以上である必要があります:
upload_max_filesize = 20M
post_max_size = 25M
post_max_sizeはupload_max_filesizeより大きくする必要があります。POSTボディ全体をカバーするため、フォーム送信内の20MBファイルはフィールドデータを含めると合計で20MBをわずかに超えます。
PHP-FPMを再起動して適用します:
sudo systemctl restart php8.1-fpm
別のプロキシの背後にいる場合(AWS ALB、Cloudflareなど)
NginxがロードバランサーやCDNの背後にある場合、その外側のレイヤーが独自のサイズ制限を設けている可能性があります:
- AWS ALB:EC2/コンテナターゲットへの実質的な上限なし — 通常は原因ではない
- Cloudflare Free/Pro:デフォルトで100MBのアップロード制限
- AWS API Gateway:10MBの上限が固定 — この値は変更不可
見分け方:Nginxのシンプルなテキストレスポンスではなく、Cloudflareブランドのエラーページで413が表示される場合、ブロックはCloudflare側で発生しています。nginx.confをどう変更しても解決しません。
修正の確認
curlを使って既知のサイズのファイルで実際にテストします:
# 15MBのテストファイルを生成する
dd if=/dev/urandom of=/tmp/testfile bs=1M count=15
# アップロードを試みる
curl -v -X POST https://example.com/api/upload \
-F "file=@/tmp/testfile" 2>&1 | grep -E "< HTTP|413|200"
修正が成功した場合:
< HTTP/1.1 200 OK
まだ413が表示される場合は、リロードが実際に反映されたか確認します:
# アクティブな設定がリロードされたか確認する
nginx -T | grep client_max_body_size
# Nginxが想定する設定で動作しているか確認する
sudo nginx -t
リクエストを再送しながらエラーログをリアルタイムで確認します — まだ何がブロックしているか正確にわかります:
sudo tail -f /var/log/nginx/error.log
サイズの推奨値
- APIエンドポイント(JSONのみ):1〜4MBに抑える。50MBのJSONペイロードはサイズ制限の問題ではなく設計上の問題です。
- 画像アップロード:10〜20MBで最新カメラのRAW写真を含むほとんどのケースに対応できます。
- 動画/ドキュメントアップロード:用途に応じて50〜200MB。50MBを超える場合はチャンクアップロードを検討してください — 不安定な接続でもより確実に機能します。
- 内部管理ツール:アクセスできるユーザーを完全に制御できるため、より高い値を設定できます。
公開エンドポイントではclient_max_body_sizeを必要以上に高く設定しないでください。大量の過大サイズリクエストはバックエンドワーカーをすぐに圧迫します — DoSの格好の攻撃経路になります。機能が許す限り制限を厳しくスコープしてください。

