실제 전송 노트: 413이 Nginx에서 났는지 앱에서 났는지 확인했습니다
2026년 10월 12일 별도 Docker의 Nginx 1.30.4·Node.js 24.21.0(Linux arm64)에 비민감 A 바이트로 만든 본문을 실제 전송했습니다. 아래 기존 인코딩 계산과는 별개인 HTTP 재현 시험입니다. 실제 고객 파일·영상·운영 서버는 사용하지 않았습니다.
작은 시험 한도로 경계와 차단 계층을 분리했습니다
Nginx 시험 서버 두 곳의 client_max_body_size를 각각 3k(3072바이트), 6k(6144바이트)로 정했습니다. 같은 앱은 요청 본문을 실제로 센 뒤 4096바이트 초과이면 413을 반환하게 작성했습니다. 앱의 4096바이트는 이번 예제의 정책이지 Node.js나 모든 프레임워크의 기본값이 아닙니다.
| 입력 / Nginx 한도 | 본문 바이트 | 실제 결과 |
| 원시 본문 / 3k | 3072 | 200, 앱 도달 |
| 원시 본문 / 3k | 3073 | 413, 앱 미도달 |
| 2500바이트를 base64 JSON으로 / 3k | 3347 | 413, 앱 미도달 |
| 3072바이트 파일의 multipart / 3k | 3234 | 413, 앱 미도달 |
| 같은 multipart / 6k | 3234 | 200, 앱 도달 |
| 원시 본문 / 6k | 4097 | 413, 앱 도달 후 앱이 거절 |
JSON은 공백 없는 단일 data 필드였습니다. multipart에는 고정 경계 adsense-lab-boundary, 필드명 file, 파일명 sample.bin, 파일 Content-Type 헤더를 사용했습니다. 이번 162바이트의 부가 크기는 이 고정 구성에서 나온 값입니다. 파일명·필드·경계를 바꾸면 달라지므로 모든 업로드에 162를 더하는 규칙으로 쓰지 않습니다.
응답 코드보다 앱 도달 여부가 수정 위치를 알려줬습니다
3073바이트 본문을 3k 서버로 보냈을 때 실제 Nginx 오류 로그에는 client intended to send too large body: 3073 bytes가 남았습니다. 접근 로그의 upstream 주소·상태는 비어 있었고, 같은 시험 식별자의 앱 요청 기록도 없었습니다. 이번에는 앞단 Nginx가 거절한 것입니다.
반대로 Nginx를 6k로 둔 채 4097바이트를 보내면 앱 수신·본문 크기 기록이 있었고 Nginx의 upstream_status도 413이었습니다. Nginx 한도를 늘린 것만으로는 앱의 4096바이트 정책이 바뀌지 않았습니다. 허용 정책을 바꾸려면 실제 차단 계층의 제한을 확인해야 하며, 413이 보인다는 이유만으로 Nginx 설정을 반복 수정할 필요는 없습니다.
앱 로그가 없다는 사실만으로 언제나 앞단 차단이라고 단정할 수는 없습니다. 기록 누락이나 다른 앱 인스턴스로의 전달 가능성도 있으므로 운영에서는 요청 식별자·로그 수집 상태·전체 요청 경로를 함께 확인하세요. 이 실험은 Content-Length를 보낸 작은 본문에 한정되며 chunked 요청, CDN 제한, 실제 업로드 저장·파일 검사·동시성은 검증하지 않았습니다.
재현 코드와 정확한 multipart 조립 방식·결과는 아래 첨부 파일에 있습니다. 3k·6k는 빠른 경계 검사용 값이며 운영 권장 용량이 아닙니다. 공식 제한 설명은 Nginx client_max_body_size에서 확인했습니다(확인일: 2026-10-12). 아래 기존 계산표와 운영 점검 절차는 보존했습니다.
이하 기존 점검 가이드
작은 파일은 올라가는데 큰 파일만 413 Request Entity Too Large로 실패한다면 요청 경로 어딘가의 본문 크기 제한을 넘은 것입니다. Nginx의 client_max_body_size가 대표 원인이지만 CDN, 로드밸런서, 두 번째 프록시, 애플리케이션 프레임워크에도 별도 제한이 있을 수 있습니다. 제한을 무작정 크게 만들기 전에 오류를 반환한 계층을 찾는 것이 먼저입니다.
1. 실패 파일 크기와 413을 반환한 계층을 확인한다
같은 형식의 작은 파일과 큰 파일을 각각 전송해 임계값을 좁히세요. 브라우저 개발자 도구의 응답 헤더, Nginx 접근·오류 로그, 애플리케이션 로그의 요청 도달 여부를 비교하면 어느 계층에서 차단됐는지 알 수 있습니다. 애플리케이션 로그에 요청이 없다면 앞단 프록시나 CDN을 먼저 봅니다.
증상우선 확인할 위치확인 방법
| 앱 로그에 요청이 없음 | CDN·로드밸런서·Nginx | 각 계층 접근 로그와 응답 헤더 |
| Nginx 오류 로그에 too large | client_max_body_size | 실제 적용 설정과 컨텍스트 |
| Nginx는 전달, 앱이 413 | 프레임워크·업로드 미들웨어 | 애플리케이션 제한 설정 |
| 일정 시간 후 실패 | 본문 크기보다 타임아웃 가능성 | 상태 코드와 처리 시간 분리 |

파일 크기와 전송한 요청 본문 크기는 다릅니다
Nginx의 제한은 파일 한 개가 아니라 클라이언트 요청 본문에 적용됩니다. multipart/form-data 본문에는 파일 외에 경계 구분자, 각 파트의 헤더와 폼 필드가 포함됩니다. multipart 형식의 부가 크기는 필드·파일명·경계에 따라 달라지므로 모든 업로드에 고정된 몇 바이트를 더한다고 가정하지 마세요.
파일을 base64 문자열로 만들어 JSON에 담는 경우도 구분해야 합니다. base64 인코딩은 원본 3바이트를 4문자로 표현합니다. 아래는 모두 A 바이트인 비민감 데이터를 만든 뒤 공백 없는 단일 file 필드 JSON으로 인코딩해 측정한 예시입니다. data URL 접두어, 다른 필드, HTTP 헤더는 포함하지 않았습니다.
| 파일 원본 바이트 | base64 바이트 | JSON 본문 바이트 |
| 1000000 | 1333336 | 1333347 |
| 4000000 | 5333336 | 5333347 |
| 10000000 | 13333336 | 13333347 |
10,000,000바이트 파일이 이 방식에서는 13,333,347바이트 본문이 됩니다. 앱의 파일 한도, JSON 파서의 본문 한도, 프록시의 본문 한도는 같은 숫자로 복사할 대상이 아닙니다. 허용할 파일 정책과 실제 전송 형식을 먼저 정한 뒤 필요한 본문 크기를 계산하고 허용 경계를 시험하세요.
2026년 10월 12일 Node.js 24.6.0, macOS arm64에서 원본 0·1·2·3바이트와 표의 3개 크기를 계산하고 디코딩 왕복 일치를 확인했습니다. 실제 파일 업로드, multipart 전송, Nginx 413 재현이나 운영 서버 변경을 수행한 시험은 아닙니다.
2. client_max_body_size의 실제 적용 위치를 찾는다
Nginx 공식 문서상 client_max_body_size 기본값은 1m이며 http, server, location 컨텍스트에 둘 수 있습니다. 더 구체적인 블록의 값이 적용되므로 다른 파일의 location /upload가 예상보다 작은 제한을 가질 수 있습니다. nginx -T로 include된 파일까지 펼친 최종 설정을 확인하세요.
nginx -T | grep -n client_max_body_size
server {
client_max_body_size 100m;
location /upload/ {
proxy_pass http://app:8080;
}
}
0은 크기 검사를 끄지만 업로드 남용과 디스크·메모리 고갈 위험이 커질 수 있습니다. 서비스가 실제 허용할 최대치보다 조금 큰 명시적 제한을 권장합니다.
3. 설정 문법 검사 후 재로드한다
설정 파일을 수정한 뒤 바로 컨테이너를 재시작하기보다 문법 검사를 먼저 실행하세요. 검사가 성공하면 reload로 기존 연결 영향을 줄일 수 있습니다. 오류가 나면 파일 경로와 중복 지시어를 고친 뒤 다시 검사합니다.
nginx -t
nginx -s reload
4. Docker에서는 수정한 파일이 실행 컨테이너에 반영됐는지 본다
호스트의 설정을 고쳤지만 컨테이너에 다른 파일이 마운트됐거나 이미지 내부 기본 설정을 쓰는 경우가 많습니다. 컨테이너 안에서 nginx -T를 실행해 실제 값을 확인하세요. Compose의 볼륨 경로, 읽기 전용 옵션, 재생성 여부도 함께 봅니다.
docker compose exec nginx nginx -T
docker compose exec nginx nginx -t
docker compose exec nginx nginx -s reload
5. 애플리케이션과 CDN 제한을 같은 허용 정책으로 맞춘다
Node, Spring, PHP, Django 같은 프레임워크나 업로드 라이브러리는 자체 본문·멀티파트 제한을 둘 수 있습니다. 앞단 Nginx를 100MB로 열어도 앱이 10MB만 허용하면 다시 실패합니다. CDN과 로드밸런서는 변경 불가능한 최대 한도가 있을 수 있으므로 공식 사양을 확인하고, 대용량 파일은 객체 스토리지의 사전 서명 URL로 직접 업로드하는 구조도 검토하세요.
6. 운영 점검 체크리스트
- 실패 파일의 실제 바이트 크기와 응답 상태를 기록합니다.
- 요청이 CDN, 로드밸런서, Nginx, 앱 중 어디까지 도달했는지 확인합니다.
- nginx -T로 최종 적용된 컨텍스트와 값을 찾습니다.
- 서비스 요구보다 조금 큰 제한을 설정하고 nginx -t를 통과시킵니다.
- 프레임워크·멀티파트·객체 스토리지 제한을 같은 기준으로 맞춥니다.
- 허용 크기 바로 아래와 위 파일로 성공·실패 경계를 시험합니다.
- 인증, 파일 형식 검사, 저장 용량 모니터링을 함께 적용합니다.
413이 아니라 프록시 연결 실패로 502가 보인다면 Nginx 502 Bad Gateway와 Docker upstream 점검법을 참고하세요.
7. 자주 묻는 질문
Q. client_max_body_size를 0으로 두면 해결되나요?
크기 검사를 끌 수 있지만 무제한 업로드 위험이 생깁니다. 업무상 최대치를 정하고 명시적으로 제한하는 편이 안전합니다.
Q. 설정을 바꿨는데 계속 413이 납니다.
다른 server/location 블록, 컨테이너 내부 설정, CDN 또는 애플리케이션 제한이 적용되는지 확인하세요.
Q. 큰 파일은 항상 Nginx를 거쳐야 하나요?
객체 스토리지의 사전 서명 URL을 사용해 브라우저가 직접 업로드하고 앱은 권한과 메타데이터만 관리하는 방식도 가능합니다.
공식 자료: Nginx client_max_body_size · Nginx 명령행 매개변수와 설정 검사 · Nginx reload 절차 (확인일: 2026-09-23)
'소프트웨어개발' 카테고리의 다른 글
| 기업용 비밀번호 관리자 선택 기준: SSO·SCIM·MFA·감사 로그까지 확인하기 (0) | 2026.09.24 |
|---|---|
| Docker 컨테이너가 Restarting을 반복할 때: exit code·로그·재시작 정책 점검 (0) | 2026.09.23 |
| 기업용 클라우드 백업 선택법: RPO·RTO·불변 백업·복구 테스트 체크리스트 (0) | 2026.09.23 |