---
title: "업로드 도중 413을 먼저 보내도 브라우저는 파일을 끝까지 보낸다, Connection close 의 엔진별 차이"
description: "본문을 받기 전에 413 으로 거절해도 대부분의 브라우저는 100MB 를 끝까지 보냅니다. Connection close 를 붙이면 전송은 줄지만 Firefox 는 413 대신 네트워크 오류를 냅니다."
date: 2026-11-05
updated: 2026-11-05T05:36:21.418Z
tags: [http, nodejs, upload, browser, express]
canonical: https://blog.wooncloud.com/posts/early-413-upload-connection-close-browsers
---

용량 한도가 있는 파일 업로드 API에 조기 거부를 넣었습니다. 본문(multipart)을 다 받기 전에 `Content-Length`만 보고 한도를 확실히 넘으면 413을 먼저 돌려주는 방식입니다. 원래는 업로드 미들웨어(multer)가 파일을 스토리지까지 다 올린 뒤에야 거절해서, 그 비용을 없애려는 것이었습니다.

기대는 이랬습니다. 큰 파일을 끝까지 받지 않으니 전송량과 시간이 줄겠죠?

## 413을 먼저 보내면 브라우저는 멈출까

궁금한 건 두 가지였습니다. 이미 날아오고 있는 본문을 서버가 어떻게 처리하는지, 그리고 브라우저가 413을 제대로 받는지.

Node 24 HTTP 서버(Express)가 본문을 읽지 않고 곧바로 `res.statusCode = 413; res.end(...)` 하게 만들었습니다. 여기에 브라우저 XHR로 100MB `FormData`를 올렸고, Playwright로 세 엔진을 모두 쟀습니다.

| 엔진 | 연결 유지(기본) | `Connection: close` 추가 |
|---|---|---|
| Chromium | 100MB 다 보낸 뒤 413 | 0.8MB 에서 413 |
| WebKit | 약 1MB 에서 멈추고 413 | 0MB 에서 413 |
| Firefox | 100MB 다 보낸 뒤 413 | 413 대신 네트워크 오류 |

비교용으로 curl도 돌려 봤습니다. curl은 조기 응답을 받으면 바로 전송을 멈춥니다(0.5MB). Node의 `http.request` 클라이언트는 서버가 읽지 않으니 backpressure로 멈춰 섭니다.

![연결을 유지하면 브라우저가 100MB를 끝까지 보내고 413을 정확히 받지만, Connection: close 를 붙이면 전송은 곧 멈추는 대신 Firefox 는 413 대신 네트워크 오류를 받는다.](/images/posts/early-413-upload-connection-close-browsers/cee0fe8e-50e7-4a35-9b9a-f8f646c38044.svg)

## 왜 연결을 유지하면 다 보낼까

연결을 유지하면 Node는 응답을 보낸 뒤에도 남은 요청 본문을 읽어서 버립니다. keep-alive 연결을 재사용하려면 본문 경계를 끝까지 읽어야 하기 때문입니다.

그리고 대부분의 브라우저는 요청 본문을 다 보낸 뒤에야 응답을 읽습니다. 서버가 413을 일찍 보내도 클라이언트는 100MB를 끝까지 올리는 거죠. 그래도 세 엔진 모두 413은 정확히 받습니다.

## Connection: close 를 붙이면

연결을 끊으면 전송량은 확 줄어듭니다. Chromium과 WebKit은 413을 제대로 받습니다.

문제는 Firefox입니다. 업로드 도중 끊긴 연결을 네트워크 오류로 처리해서, 앱이 용량 초과라는 이유를 사용자에게 보여 줄 수 없습니다. 응답 코드로 안내 문구를 고르는 클라이언트라면 사용자는 용량 부족 대신 업로드 실패를 보게 됩니다.

참고로 `Expect: 100-continue`는 서버가 본문 전에 거부할 수 있는 HTTP 표준 장치지만, 브라우저 fetch와 XHR은 이 헤더를 보내지 않습니다.

## 그래서 조기 413으로 뭘 얻나

**조기 거부로 줄어드는 건 전송량이 아니라 서버 뒤쪽 작업입니다.** 스토리지 업로드와 삭제, DB 쓰기가 0회가 되고 응답도 정확합니다.

![업로드를 다 받은 뒤 거부하면 스토리지 업로드·삭제·DB 쓰기가 전부 일어나지만, 본문을 받기 전에 조기 거부하면 그 작업이 0회가 된다.](/images/posts/early-413-upload-connection-close-browsers/d343e28b-9838-4254-a8e0-c8b56e7287d6.webp)

전송량까지 줄이고 싶다면 연결을 끊는 대신 다른 층에서 풉니다. 첫 413을 받은 클라이언트가 남은 대기 파일 중 확실히 안 들어가는 것만 건너뛰게 하거나, 업로드 전에 용량을 조회해 미리 막는 방식입니다. presigned 방식이라면 발급 단계에서 거부하면 됩니다.

프록시(CDN, 로드밸런서)가 앞에 있으면 동작이 또 달라질 수 있습니다. 실제 경로에서 한 번 더 재 봐야 합니다.

## 참고

- https://www.rfc-editor.org/rfc/rfc9110#section-15.5.14
- https://www.rfc-editor.org/rfc/rfc9110#section-10.1.1
- https://www.rfc-editor.org/rfc/rfc9112#name-tearing-down-connections
- https://nodejs.org/api/http.html#class-httpincomingmessage
