문제

POST나 PUT을 보냈는데 서버가 작업을 하기도 전에 411 Length Required로 튕겨냅니다. 본문은 요청 안에 그대로 있는데 서버가 손도 안 댑니다. 무엇을 보냈는지가 아니라, 무엇을 선언하지 않았는지 — 본문의 크기를, 미리, Content-Length 헤더로 — 를 문제 삼는 것입니다.

증상

  • POST/PUT/PATCH가 애플리케이션 레벨 오류 없이 곧바로 411을 반환합니다.
  • 같은 호출이 curl이나 Postman에서는 되는데 코드에서는 실패합니다(또는 그 반대).
  • 스트리밍/청크 업로드에서 발생합니다 — 파일, 스트림, 제너레이터를 요청 본문에 파이핑할 때.
  • 어떤 클라이언트는 빈 본문 POST(보낼 게 없는 폼 제출이나 웹훅)에서 걸립니다.
  • 실패한 요청이 Content-Length 대신 Transfer-Encoding: chunked를 담고 있습니다.

이 오류의 실제 의미

HTTP/1.1은 요청 본문을 프레이밍하는 두 방법을 줍니다. Content-Length: 4096을 보내고 정확히 4096바이트를 보내거나 — 고정된, 알려진 크기. 아니면 Transfer-Encoding: chunked를 보내고 본문을 조각으로 스트리밍하며 0바이트 청크로 끝내거나 — 총량을 결코 확정하지 않고. 청크 형태는 얼마나 될지 알기 전에 보내기 시작할 수 있도록 존재합니다.

RFC 9110 §15.5.12는 411을 서버가 두 번째 옵션을 거부하는 것으로 정의합니다: 서버가 “정의된 Content-Length 없이는 요청을 받기를 거부”하는 것. 서버는 한 바이트를 읽기 전에 크기를 알고 싶어 합니다 — 보통 버퍼를 미리 할당하거나, 최대 요청 크기를 강제하거나, 알 수 없는 길이의 스트림을 감당 못 하는 무언가(레거시 핸들러, 엄격한 WAF, 스토리지 API)에 본문을 넘기기 위해서.

중요한 단어는 *정의된(defined)*입니다. 이건 본문이 비었거나 잘못됐다는 게 아닙니다. 서버가 미리 볼 수 없는 길이의 본문을 받기를 거절하는 것입니다. RFC는 처방도 똑같이 명시합니다: 클라이언트는 “유효한 Content-Length 헤더 필드를 추가하면 요청을 반복해도 된다.” 그러니 411은 막다른 길이 아니라 구체적 지시입니다. 그걸 버퍼링하고, 바이트를 세고, 서버에 숫자를 알려주세요.

주요 원인 3가지

  1. HTTP 클라이언트가 본문을 청크로 스트리밍한다. 흔한 경우입니다. 많은 HTTP 라이브러리에 파일 객체, 스트림, 제너레이터를 요청 본문으로 넘기면 길이를 계산하려고 전체를 읽는 대신 기본으로 Transfer-Encoding: chunked를 씁니다. Content-Length를 요구하는 서버 상대로는 그런 업로드마다 411이 납니다. 단서: 고정 문자열이나 바이트 버퍼를 넘기면 되고 스트림을 넘기면 실패합니다.
  2. 길이 헤더 없는 빈 본문 POST. 어떤 클라이언트는 보낼 본문이 없으면 Content-Length를 아예 생략합니다 — 맨 폼 제출, 웹훅 핑, 트리거로 쓰는 POST. 엄격한 서버는 “Content-Length 없음, 청크 인코딩 없음”을 “이 요청 본문이 어디서 시작하고 끝나는지 모르겠다”로 읽고 411을 반환합니다. 수정은 헤더 하나: Content-Length: 0.
  3. 청크 요청 본문을 거부하는 엄격한 서버나 미들박스. 구형 IIS 설정, 일부 Java 서블릿 컨테이너, 특정 오브젝트 스토리지 엔드포인트, 하드닝된 WAF는 일부러 청크 요청을 거부합니다 — 버퍼링 전에 한도를 강제하려고 길이를 원하니까요. 당신 클라이언트는 합리적으로 행동하는 것이고, 서버가 스트리밍 경로를 선택 해제하고 411로 그걸 알리는 것뿐입니다.

DechoNet으로 진단

  • HTTP 진단은 응답이 오리진에서 온 진짜 411인지, 잘못 읽은 400이나 413이 아닌지 확인합니다 — 셋은 늘 뒤섞이고, 수정이 정반대입니다. 정확한 상태 코드와 응답 헤더를 보는 게 클라이언트를 건드리기 전 첫 단계입니다.
  • 진단이 응답 헤더를 보여주니 프록시, CDN, WAF가 경로에 있는지 볼 수 있습니다. 특정 엣지를 통과할 때만 나타나는 411은 오리진이 아니라 미들박스 정책을 가리킵니다 — 업로드 코드를 다시 쓰기 전에 알아둘 가치가 있습니다.

해결 체크리스트

  • 정말 411인지 확인, 413(너무 큼)이나 431(헤더 너무 큼)이 아니라. 수정이 완전히 갈립니다: 411은 길이를 추가, 413은 본문을 축소, 431은 헤더를 다듬는 것.
  • 청크 대신 Content-Length를 보내라. HTTP 클라이언트에서 본문을 완전히 버퍼링하고, 바이트 길이를 재고, 헤더를 명시적으로 설정하세요. 구체적으로: 클라이언트에 스트림을 넘겨 청크되게 하지 말고, 파일/스트림을 문자열이나 바이트 배열로 읽어 들인 뒤 보내세요.
  • 빈 POST면 Content-Length: 0을 설정. 요청에 정말 본문이 없으면 생략하지 말고 명시적 0 길이 헤더로 그렇게 말하세요.
  • 라이브러리의 스트리밍 동작을 확인. 많은 클라이언트가 스트림/제너레이터 본문에 자동으로 청크로 전환합니다. 고정 버퍼를 넘기거나, 길이를 계산해 보내는 클라이언트 옵션을 쓰세요.
  • 서버를 통제하고 이걸 원치 않으면, 청크 요청 본문을 받을지 결정하세요. 일부 스택은 활성화할 수 있고, 대가는 버퍼링 전에 크기 한도를 강제하는 능력을 잃는 것입니다.
  • 같은 경로로 다시 테스트. WAF나 프록시가 411을 강제하는 거라면 직접-오리진 호출이 아니라 실제 엣지 상대로 수정을 검증하세요.

에스컬레이션 시점

  • 클라이언트가 이미 올바른 Content-Length를 보내는데도 411이 나면 서비스 앞단의 프록시나 WAF 운영자에게 넘기세요 — 미들박스가 오리진에 닿기 전에 헤더를 벗기거나 재작성할 수 있고, 그쪽 로그만이 그걸 보여줍니다.
  • 서드파티 API가 문서화된 스트리밍 업로드에 411을 반환하면 버그로 단정하기 전에 필수 Content-Length(또는 multipart/resumable 대안)를 문서에서 확인하세요 — 많은 스토리지 API가 일부러 선언된 크기를 요구하고 별도의 청크 업로드 프로토콜을 따로 제공합니다.

내 도메인에서 바로 확인

무료, 가입 불필요. 이 가이드가 다루는 항목을 바로 검사하고 조치 방법을 알려드립니다.