업로드만 413이면 파일이 깨진 게 아니라 서버 액션 기본 1MB 또는 버셀 함수 4.5MB 본문 한도를 넘긴 줄입니다. bodySizeLimit으로 올리는 한도와 플랫폼 한도를 나누고, 더 큰 파일은 함수 밖으로 보냅니다. Next.js, Vercel, nginx, 한국 1인 개발자 기준. 2026년 9월 serverActions와 Functions 한도 문서.
같은 413이라도 자른 주체가 다릅니다. 넥스트 서버 액션은 요청 본문 기본 한도가 1MB입니다. 2026년 9월 serverActions 문서는 큰 본문 파싱이 서버 자원을 잡아먹는 것을 막기 위한 기본값이라고 적고, experimental.serverActions.bodySizeLimit으로 바꿉니다. 문자열 '2mb'나 '500kb'처럼 bytes가 읽는 형식을 넣습니다.
버셀에 올린 함수는 그 위에서 한 번 더 잘립니다. Vercel Functions 한도 문서는 요청 본문과 응답 본문의 최대를 4.5MB로 적고, 넘으면 413: FUNCTION_PAYLOAD_TOO_LARGE를 돌려준다고 합니다. 넥스트 설정을 10MB로 올려도 이 플랫폼 한도는 그대로입니다. 로컬 next dev에서 2MB 액션이 1MB 때문에 막히고, 한도를 3MB로 올린 뒤 배포하면 4.5MB 사진에서 다시 막히는 순서가 흔합니다.
연결이 중간에 끊긴 ECONNRESET과는 상태 코드가 다릅니다. 리셋은 응답이 오기 전에 소켓이 닫힌 줄이고, 413은 본문 크기를 보고 거절한 줄입니다. 리셋 쪽은 ECONNRESET 글을 보면 됩니다.
같은 413이라도 액션 한도, 플랫폼 한도, 프록시 한도 중 어디서 잘렸는지가 다르다. 자체 인포그래픽
본문을 자르는 세 관문
액션, 버셀 함수, 앞단 프록시를 순서대로 봅니다.
첫 관문은 서버 액션입니다. 한도는 원시 HTTP 본문 전체에 적용됩니다. multipart 경계와 파트 헤더가 더해지므로, 문서가 말하는 여유는 보통 10KB에서 20KB입니다. 파일이 한도와 거의 같으면 파일만 재면 통과할 것 같아도 거절됩니다. 두 번째 관문은 버셀 4.5MB입니다. 응답이 너무 커도 같은 계열의 한도에 걸리고, 그때는 요청이 아니라 돌려주는 JSON이 큰 경우입니다. 세 번째 관문은 직접 띄운 nginx입니다. client_max_body_size 기본값은 1m이라, 노드 로그가 비어 있는데 브라우저만 413 HTML이면 프록시가 먼저 자른 것입니다.
어디 한도인가
기본 숫자
보이는 말
서버 액션
1MB
body size limit, serverActions 문서 주소
버셀 함수
4.5MB
FUNCTION_PAYLOAD_TOO_LARGE
Pages API bodyParser
1mb
pages/api의 config sizeLimit
nginx
1m
앱 로그 없이 413 HTML
Pages 라우터의 pages/api는 export const config 안 bodyParser.sizeLimit 기본이 1mb입니다. 앱 라우터 Route Handler는 그 config를 쓰지 않고 요청 스트림을 직접 읽지만, 버셀에 올리면 4.5MB 플랫폼 한도는 그대로 적용됩니다. 앱 라우터로 옮겼다고 업로드 한도가 사라지지는 않습니다.
프로필 이미지처럼 1MB를 조금 넘는 파일은 bodySizeLimit을 '2mb'로 올리면 액션이 통과합니다. 여유 10KB에서 20KB를 빼고 고르세요. 3MB짜리 휴대폰 원본은 버셀에서 함수를 통과시키는 한 4.5MB 안에 들 수 있지만, 원본이 5MB를 넘으면 설정을 더 올려도 FUNCTION_PAYLOAD_TOO_LARGE가 납니다. 그 경우는 한도를 푸는 것이 아니라 파일이 함수 본문에 실리지 않게 해야 합니다.
버셀 지식 문서는 큰 업로드를 함수에 넣지 말고 저장소로 브라우저가 직접 올리라고 안내합니다. 서버는 짧은 토큰만 주고, 파일 바이트는 Blob 같은 저장소로 바로 갑니다. 그러면 4.5MB 함수 한도를 우회합니다. 자체 서버에 nginx만 있다면 client_max_body_size를 액션 한도보다 작지 않게 맞춥니다. 프록시가 1m인데 액션만 10mb이면 요청은 노드에 도착하기 전에 잘립니다.
[ ] 네트워크 탭에서 상태 413과 응답 문장을 봤다
[ ] 파일 크기와 multipart 여유를 더했다
[ ] 1MB 근처면 bodySizeLimit을 문서 형식으로 올렸다
[ ] 4.5MB를 넘으면 함수를 통과시키지 않는 업로드로 바꿨다
[ ] nginx를 쓰면 client_max_body_size를 같이 봤다
4.5MB를 넘는 파일은 함수 본문에 실리지 않게 브라우저에서 저장소로 보낸다. 자체 인포그래픽
로컬은 되고 프리뷰만 413일 때
프리뷰만 실패하면 플랫폼 4.5MB를 먼저 의심합니다.
로컬에서 3MB가 통과했다는 것은 액션 한도를 이미 그 위로 올렸다는 뜻에 가깝습니다. 같은 파일을 프리뷰에 올리자 FUNCTION_PAYLOAD_TOO_LARGE가 보이면 넥스트 설정은 더 올려도 소용이 없습니다. 버셀 한도 문서의 숫자는 요청과 응답 둘 다입니다. 업로드가 작은데 413이면, 함수가 돌려주는 JSON이 4.5MB를 넘었는지도 보세요. 목록 전체를 한 번에 내려 주는 핸들러가 이쪽입니다.
응답이 HTML 오류 페이지라 클라이언트의 JSON.parse가 <에서 멈추면, 크기 거절이 파서 오류로 보이기도 합니다. 파서만 고치면 다시 413입니다. 그 순서는 JSON.parse 글과 같이 보면 됩니다. 브라우저가 다른 출처의 업로드 주소로 못 가면 413 대신 CORS가 나므로, 출처가 막힌 줄은 CORS 글에서 헤더를 확인하면 됩니다.
휴대폰 사진이 유독 걸리는 이유
최근 폰 원본은 한 장이 3MB에서 8MB인 경우가 많습니다.
폼 테스트에 쓰던 100KB 샘플은 1MB 액션 한도를 통과합니다. 같은 폼에 카메라 원본을 넣으면 로컬에서 먼저 1MB에 걸리고, 한도를 올린 뒤 버셀에서 4.5MB에 걸립니다. 국내 통신사 망이나 카카오에서 줄인 사진과는 별개로, 제한은 서버가 받은 바이트 수입니다. 네트워크 탭의 Request Payload 또는 Content-Length가 한도보다 큰지 보면 감으로 재지 않아도 됩니다.
액션 한도를 아주 크게 올리는 것은 문서가 막으려는 자원 소모를 다시 여는 일입니다. 통과해야 하는 최대 파일에 20KB 정도만 더해 적고, 그 위를 넘는 파일은 리사이즈하거나 직접 업로드로 빼세요. 웹훅 JSON처럼 수십 KB인 카카오, 토스 알림은 이 한도와 거의 상관이 없습니다. 파일 필드가 있는 폼만 먼저 보면 됩니다.
숫자 두 개 | 서버 액션 기본은 1MB, 버셀 함수 요청과 응답은 4.5MB입니다. 넥스트 설정은 첫 숫자만 바꿉니다. 두 번째 숫자는 파일을 함수 밖으로 보내야 넘어갑니다.