이미지 업로드가 413으로 막힌 이유 — 앱이 아니라 프록시가 거절했다
문제 발생
로컬에서는 잘 되던 이미지 업로드가 운영에서만 실패했습니다.
413 Request Entity Too Large애플리케이션 로그에는 아무 기록도 없었습니다. 앱의 업로드 제한은 10MB로 넉넉히 잡아둔 상태였습니다.
원인 분석
요청이 앱까지 오지 않았습니다. 앞단 nginx가 먼저 거절한 것입니다.
nginx 문서의 client_max_body_size 설명 그대로입니다 — 클라이언트 요청 본문의 최대 허용 크기를 설정한다. 요청의 크기가 설정된 값을 초과하면 413(Request Entity Too Large) 오류가 클라이언트에 반환된다. 그리고 **기본값은 1m**입니다.
로컬에서 재현되지 않은 이유도 이것입니다 — 개발 서버는 nginx 없이 앱에 직접 붙습니다. 프록시가 있는 환경과 없는 환경의 차이이지 코드 차이가 아닙니다.
문서에는 실무에서 자주 부딪히는 주의사항도 있습니다 — 브라우저가 이 오류를 제대로 표시하지 못한다는 점입니다. 사용자에게는 그냥 실패한 것처럼 보입니다.
해결 방안
- 업로드를 받는 경로에만 올립니다. 서버 전체를 열지 않습니다.
location /uploads/ {
client_max_body_size 10m;
proxy_pass http://127.0.0.1:3000;
}-
앱의 제한과 숫자를 맞춥니다. 두 값이 다르면 어느 쪽에서 잘리는지에 따라 오류 형태가 달라져 디버깅이 어려워집니다. nginx가 조금 더 크게 잡아 앱이 자기 언어로 거절하게 하는 편이 사용자 메시지 측면에서 낫습니다.
-
사용자에게 이유를 보여줍니다. 413 응답을 앱의 오류 페이지로 매핑하거나, 업로드 전에
File.size로 클라이언트에서 먼저 확인해 "20MB 이하만 올릴 수 있습니다" 같은 안내를 줍니다. -
0은 신중하게 씁니다. 문서 설명대로0은 검사를 완전히 끕니다 — 메모리·디스크 소진 공격의 문이 됩니다. -
다른 제한도 함께 봅니다. 큰 파일 업로드에서는
client_body_timeout,proxy_read_timeout, 그리고 앱 프레임워크의 자체 본문 제한이 차례로 문제가 됩니다. -
정말 큰 파일은 프록시를 거치지 않게 합니다. 오브젝트 스토리지의 presigned URL로 브라우저가 직접 올리면 이 제한도, 서버 대역폭도 문제가 되지 않습니다.
댓글0
댓글을 남기려면 로그인이 필요해요. 로그인
아직 댓글이 없어요. 첫 의견을 편하게 남겨 보세요.