413 Request Entity Too Large 에러의 원인과 해결방법 | Nginx・PHP・Apache 완전 대응
파일 업로드 기능을 구현하다 보면 갑자기 브라우저에 「413 Request Entity Too Large」라는 오류가 표시될 수 있습니다. 이 오류는 전송한 요청 본문의 크기가 서버에 설정된 상한값을 초과했을 때 반환되는 HTTP 상태 코드입니다. 본 문서에서는 Nginx·PHP·Apache 각각의 설정 위치를 망라하여 원인 파악부터 해결까지 체계적으로 설명합니다.
413 에러란 무엇인가
HTTP 413 상태 코드(RFC 9110에서 "Content Too Large"로 개명)는 클라이언트가 전송한 요청 본문이 서버의 처리 가능한 크기를 초과할 때 반환됩니다. 파일 업로드뿐만 아니라 큰 JSON 페이로드나 Base64 인코딩 데이터 전송에서도 발생합니다.
이 오류가 발생하면 서버가 요청 자체를 거부하므로, 애플리케이션 코드(PHP, Python 등)에 처리가 도달하기 전에 연결이 끊깁니다. 따라서 애플리케이션 로그에는 기록이 남지 않으며, 웹 서버의 에러 로그를 확인해야 합니다.
Nginx: client_max_body_size 설정
기본적으로 Nginx의 <code>client_max_body_size</code>는 <strong>1MB</strong>로 설정되어 있습니다. 이미지나 PDF 업로드를 받는 사이트의 경우, 거의 확실히 이 값을 높여야 합니다.
# /etc/nginx/nginx.conf(グローバル設定)
http {
# すべての仮想ホストに適用
client_max_body_size 100m;
server {
listen 80;
server_name example.com;
# このサーバーブロック内のみに適用(httpの値を上書き)
client_max_body_size 50m;
location /api/upload {
# このlocationのみに適用(serverの値を上書き)
client_max_body_size 200m;
proxy_pass http://backend;
}
}
}
설정의 우선순위는 <code>location</code> > <code>server</code> > <code>http</code> 순입니다. 특정 업로드 엔드포인트만 상한을 늘리고 싶다면 <code>location</code> 블록에서 지정하는 것이 모범 사례입니다.
# 設定変更後はNginxをリロード
sudo nginx -t # 構文チェック
sudo systemctl reload nginx # リロード(ダウンタイムなし)
# エラーログの確認
tail -f /var/log/nginx/error.log
# 413エラー時のログ例:
# client intended to send too large body: 10485760 bytes
PHP: upload_max_filesize / post_max_size / memory_limit의 관계
PHP에는 여러 크기 제한이 있으며, 각각의 관계를 올바르게 이해해야 합니다. 다음의 부등식을 항상 만족하도록 설정하세요.
; php.ini の設定
; 必ず以下の関係を維持すること:
; upload_max_filesize <= post_max_size <= memory_limit
; 1ファイルあたりの最大サイズ
upload_max_filesize = 50M
; POSTリクエスト全体の最大サイズ(複数ファイルの合計 + フォームデータ)
post_max_size = 100M
; PHPスクリプトが使用できる最大メモリ
memory_limit = 256M
; ファイルアップロードの最大数
max_file_uploads = 20
; アップロードの一時保存先
upload_tmp_dir = /tmp
| 지시어 | 기본값 | 영향 범위 |
|---|---|---|
upload_max_filesize |
2M | 1파일의 최대 크기 |
post_max_size |
8M | 전체 POST 요청 (파일 + 폼 데이터) |
memory_limit |
128M | 스크립트 실행 시의 최대 메모리 사용량 |
max_file_uploads |
20 | 동시에 업로드 가능한 파일 수 |
max_execution_time |
30 | 스크립트 실행의 최대 초 수 (대용량 파일은 연장이 필요) |
max_input_time |
60 | 입력 데이터 분석에 소요되는 최대 초 수 |
// 現在のPHP設定値を確認するスクリプト
echo 'upload_max_filesize: ' . ini_get('upload_max_filesize') . PHP_EOL;
echo 'post_max_size: ' . ini_get('post_max_size') . PHP_EOL;
echo 'memory_limit: ' . ini_get('memory_limit') . PHP_EOL;
echo 'max_file_uploads: ' . ini_get('max_file_uploads') . PHP_EOL;
echo 'max_execution_time: ' . ini_get('max_execution_time') . PHP_EOL;
// php.ini の場所を確認
echo 'Loaded php.ini: ' . php_ini_loaded_file() . PHP_EOL;
Apache: LimitRequestBody 설정
Apache에서는 <code>LimitRequestBody</code> 지시어로 요청 본문의 제한을 설정합니다. 기본값은 0(무제한)이지만, 보안을 위해 적절한 제한을 설정하는 것이 권장됩니다.
# /etc/apache2/apache2.conf または .htaccess
# バイト単位で指定(100MB = 104857600 bytes)
# グローバル設定
LimitRequestBody 104857600
# 特定のディレクトリのみ
<Directory "/var/www/html/uploads">
LimitRequestBody 209715200
</Directory>
# 特定のURLパスのみ
<Location "/api/upload">
LimitRequestBody 209715200
</Location>
# .htaccess での設定(AllowOverride が必要)
# php_value upload_max_filesize 50M
# php_value post_max_size 100M
흔한 조합 실수
413 에러의 까다로운 점은 여러 계층에 각각 상한 설정이 있다는 것입니다. 한 가지 설정을 변경해도 다른 계층에서 차단되는 경우가 자주 발생합니다.
케이스1: Nginx는 통과하지만 PHP에서 거부됨
Nginx의 <code>client_max_body_size</code>를 100m으로 설정했는데도 PHP의 <code>upload_max_filesize</code>가 2M으로 유지되면, 50MB 파일은 Nginx를 통과하지만 PHP 쪽에서 무시됩니다. 이 경우 HTTP 상태는 200을 반환하지만 파일은 비어 있게 되고, <code>$_FILES</code>에 에러 코드 1(UPLOAD_ERR_INI_SIZE)이 설정됩니다.
케이스2: upload_max_filesize는 충분하지만 post_max_size가 부족함
<code>upload_max_filesize = 50M</code>으로 설정하더라도 <code>post_max_size = 8M</code>으로 유지되면, 8MB를 초과하는 요청 전체가 폐기됩니다. 이 경우 <code>$_POST</code>와 <code>$_FILES</code> 모두 빈 배열이 되는 혼란스러운 동작이 발생합니다.
케이스3: 리버스 프록시 구성에서의 누락
Nginx를 리버스 프록시로 사용하고 백엔드에 Apache + PHP-FPM을 배치한 경우, Nginx, Apache, PHP 모두에서 제한을 설정해야 합니다.
# Nginx リバースプロキシの設定
server {
client_max_body_size 100m;
location / {
proxy_pass http://127.0.0.1:8080;
# プロキシ関連のタイムアウトも延長(大容量ファイル対策)
proxy_read_timeout 300;
proxy_connect_timeout 300;
proxy_send_timeout 300;
# バッファリングの設定
proxy_request_buffering on;
proxy_buffering on;
proxy_buffer_size 128k;
proxy_buffers 4 256k;
}
}
디버그 절차
413 에러가 발생했을 때는 다음 단계에서 원인을 특정합니다.
# 1. どのレイヤーでブロックされているか確認
# Nginx のエラーログを確認
tail -n 50 /var/log/nginx/error.log | grep "too large"
# 2. PHP の設定値を確認
php -i | grep -E "upload_max|post_max|memory_limit"
# 3. PHP-FPM の設定ファイルを確認(php.ini とは別の場合がある)
php-fpm -i | grep -E "upload_max|post_max"
# または
find /etc -name "php.ini" -o -name "www.conf" | xargs grep -l "upload_max"
# 4. Apache の設定を確認
apachectl -t -D DUMP_RUN_CFG 2>/dev/null | grep -i limit
grep -r "LimitRequestBody" /etc/apache2/ /etc/httpd/
# 5. curl で実際にテスト(10MBのダミーファイルを送信)
dd if=/dev/zero of=/tmp/test_10mb.bin bs=1M count=10
curl -v -X POST -F "file=@/tmp/test_10mb.bin" https://example.com/api/upload
# 6. レスポンスヘッダーを確認
curl -I -X POST -F "file=@/tmp/test_10mb.bin" https://example.com/api/upload
테스트 방법
설정을 변경한 후에는 실제로 크기가 다른 파일을 업로드하여 테스트하는 것이 중요합니다. DevLab은 경계값 테스트용 파일을 제공합니다. 상한선 전후(예: 상한이 50MB인 경우 49MB, 50MB, 51MB)로 테스트하고 예상대로 작동하는지 확인하세요.
# JavaScript での送信テスト例
# 指定サイズのBlobを作成して送信する方法
# <script>
# async function testUpload(sizeMB) {
# const blob = new Blob(
# [new ArrayBuffer(sizeMB * 1024 * 1024)],
# { type: 'application/octet-stream' }
# );
# const formData = new FormData();
# formData.append('file', blob, 'test.bin');
#
# const response = await fetch('/api/upload', {
# method: 'POST',
# body: formData
# });
# console.log(sizeMB + 'MB:', response.status, response.statusText);
# }
#
# // 境界値テスト
# testUpload(1); // 1MB - 通常は成功
# testUpload(10); // 10MB - 設定による
# testUpload(50); // 50MB - 設定による
# testUpload(100); // 100MB - 通常は失敗
# </script>
각 서버의 권장 설정 요약
| 레이어 | 지시어 | 권장값 (50MB 업로드 지원) |
|---|---|---|
| Nginx | client_max_body_size |
60m(여유를 두기) |
| PHP | upload_max_filesize |
50M |
| PHP | post_max_size |
55M (<code>upload_max_filesize</code>보다 큼) |
| PHP | memory_limit |
256M |
| Apache | LimitRequestBody |
62914560 (60MB, 바이트 단위) |
이 기사에서 사용할 수 있는 테스트 파일 (무료)
- → <a href="/ja/files/threshold/png/10mb/" class="text-primary-600 dark:text-primary-400 hover:underline">10MB 경계값 테스트 PNG</a> — Nginx 기본 한계(1MB) 초과 테스트용
- → <a href="/ja/files/threshold/png/50mb/" class="text-primary-600 dark:text-primary-400 hover:underline">50MB 경계값 테스트 PNG</a> — upload_max_filesize 상한 테스트용
- → <a href="/ja/files/threshold/" class="text-primary-600 dark:text-primary-400 hover:underline">경계값 테스트 파일 목록</a> — 다양한 크기의 경계값 테스트 파일
- → <a href="/ja/files/images/png/1mb/" class="text-primary-600 dark:text-primary-400 hover:underline">1MB 테스트 PNG 이미지</a> — 작은 크기에서의 동작 확인용
관련 기사
- → <a href="/ja/blog/nginx-upload-config/" class="text-primary-600 dark:text-primary-400 hover:underline">Nginx 파일 업로드 설정 가이드 | client_max_body_size・proxy 설정</a>
- → <a href="/ja/blog/file-validation-checklist/" class="text-primary-600 dark:text-primary-400 hover:underline">웹 폼 파일 검증 구현 체크리스트</a>
- → <a href="/ja/blog/http-422-error/" class="text-primary-600 dark:text-primary-400 hover:underline">422 Unprocessable Entity 에러의 원인과 해결방법</a>
- → <a href="/ja/blog/http-507-error/" class="text-primary-600 dark:text-primary-400 hover:underline">507 Insufficient Storage 에러의 원인과 해결방법</a>