- Published on
EKS NGINX Ingress 400·413 해결 - body·버퍼 튜닝
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
서버는 멀쩡한데 특정 요청만 NGINX Ingress에서 400(Bad Request) 혹은 413(Request Entity Too Large) 로 떨어지면, 애플리케이션 로직을 의심하기 전에 “Ingress(=NGINX)에서 요청을 받는 단계”의 제한값을 먼저 점검해야 합니다. 특히 EKS에서는 ALB/NLB, WAF, Ingress Controller, 백엔드(서비스/파드)까지 홉이 여러 개라서, 어느 계층에서 컷(cut) 나는지를 분리하지 않으면 튜닝이 헛바퀴가 되기 쉽습니다.
이 글은 EKS의 ingress-nginx(커뮤니티 NGINX Ingress Controller 기준)에서 자주 만나는 400/413을 proxy-body-size(요청 바디 크기) 와 버퍼(헤더/요청라인/업스트림 버퍼링) 관점에서 재현·진단·해결하는 실전 체크리스트입니다.
> 413이 “업로드/푸시”에서 자주 보인다면, 같은 증상을 다른 맥락에서 다룬 글도 참고할 만합니다: Git LFS 푸시 실패 413·403 원인과 해결법
1) 400 vs 413: 어떤 종류의 실패인가
413 (Request Entity Too Large)
- 요청 바디가 너무 큼: 파일 업로드, 큰 JSON, base64 이미지 등
- NGINX가 백엔드로 프록시하기 전에 바디 크기를 검사하고 차단
- ingress-nginx에서는 주로
proxy-body-size(annotation) 또는 ConfigMap의proxy-body-size가 관여
400 (Bad Request)
400은 원인이 더 다양합니다. ingress-nginx에서 흔한 케이스는:
- 요청 헤더가 너무 큼(특히 쿠키/Authorization/JWT가 커짐)
- 요청 라인(URI+쿼리)이 너무 김(긴 쿼리스트링)
- 잘못된 Host 헤더, 깨진 HTTP, TLS/HTTP 혼선 등
- gRPC/HTTP2에서 특정 프록시 설정과 충돌
즉, 413은 비교적 명확하게 “바디 크기”, 400은 “헤더/요청라인/프로토콜” 계열을 먼저 의심하는 게 빠릅니다.
2) 먼저 해야 할 일: 어디서 끊겼는지 로그로 확정
(1) ingress-nginx 컨트롤러 로그 확인
# 네임스페이스는 환경에 맞게 변경
kubectl -n ingress-nginx logs deploy/ingress-nginx-controller \
--since=30m | egrep " 400 | 413 | client sent | request header | too large" -n
로그에서 자주 보이는 힌트:
client intended to send too large body→ 413, 바디 제한client sent too large request header→ 400, 헤더 버퍼/크기upstream sent too big header→ (대개 502/500 계열이지만) 업스트림 응답 헤더 버퍼 문제
(2) 실제로 NGINX가 반환했는지 확인
응답 헤더에 server: nginx 또는 ingress-nginx 특유의 헤더가 있으면 Ingress에서 컷일 확률이 큽니다.
curl -i https://api.example.com/upload \
-H 'Expect:' \
-F 'file=@big.bin'
(3) WAF/ALB 계층도 함께 의심
EKS 앞단에 AWS WAF를 붙였다면 400/403이 섞여 보일 수 있습니다. WAF에서 차단인데 NGINX로 오해하면 튜닝이 안 먹습니다. WAF 쪽 이슈가 의심될 때는 아래 글 흐름도 도움이 됩니다.
3) 413 해결: proxy-body-size 제대로 늘리기
(1) Ingress annotation으로 서비스별 적용
가장 안전한 방법은 “전체 클러스터”가 아니라 해당 Ingress에만 상향 적용하는 것입니다.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: api
namespace: default
annotations:
nginx.ingress.kubernetes.io/proxy-body-size: "50m"
# 업로드가 느릴 경우 타임아웃도 함께(필요 시)
nginx.ingress.kubernetes.io/proxy-read-timeout: "300"
nginx.ingress.kubernetes.io/proxy-send-timeout: "300"
spec:
ingressClassName: nginx
rules:
- host: api.example.com
http:
paths:
- path: /upload
pathType: Prefix
backend:
service:
name: api-svc
port:
number: 80
- 단위는
m,k등을 사용합니다. 0은 무제한이지만, 운영에서는 권장하지 않습니다(악성/실수로 메모리/디스크 압박).
(2) ConfigMap으로 전역 기본값 변경(신중)
여러 서비스가 동일하게 큰 바디를 받아야 한다면 컨트롤러 ConfigMap을 조정합니다.
kubectl -n ingress-nginx get configmap ingress-nginx-controller -o yaml
예시:
apiVersion: v1
kind: ConfigMap
metadata:
name: ingress-nginx-controller
namespace: ingress-nginx
data:
proxy-body-size: "50m"
전역 설정은 영향 범위가 크므로, 가능하면 annotation(서비스별) → 필요 시 전역 순으로 접근하세요.
(3) 업로드 경로는 버퍼링/디스크 스풀도 고려
큰 업로드에서 413이 아니라도 지연/끊김이 있다면 요청 바디를 NGINX가 어떻게 처리하는지(버퍼링/임시파일)도 함께 봐야 합니다. 특히 SSE/스트리밍이나 긴 연결에서는 프록시 버퍼링이 부작용을 만들 수 있습니다.
- 스트리밍/버퍼링 튜닝 관점 참고: LLM SSE 스트리밍 499 502 급증과 응답 끊김을 잡는 프록시 튜닝 체크리스트
4) 400 해결: 헤더/요청라인/버퍼 제한 튜닝
413은 proxy-body-size로 끝나는 경우가 많지만, 400은 헤더 크기가 원인인 경우가 매우 흔합니다. 예를 들어:
- 쿠키에 추적/AB테스트 값이 잔뜩 붙음
- OAuth/세션 토큰이 비대해짐(JWT가 커짐)
- 프론트가 쿼리스트링에 상태를 과하게 담음
(1) “request header too large”면 large-client-header-buffers
ingress-nginx에서는 ConfigMap에서 large-client-header-buffers를 조정합니다.
apiVersion: v1
kind: ConfigMap
metadata:
name: ingress-nginx-controller
namespace: ingress-nginx
data:
large-client-header-buffers: "4 32k"
의미:
- 헤더 버퍼를 4개, 각 32KB로 확보
- 기본값은 배포판/버전에 따라 다를 수 있으나, 큰 쿠키/Authorization을 쓰는 서비스에서 400을 줄이는 데 효과적입니다.
주의:
- 무작정 크게 하면 워커당 메모리 사용량이 증가합니다.
- 근본적으로는 “헤더를 줄이는 것(쿠키 정리, JWT 축소)”이 최선입니다.
(2) “too long URI”면 요청라인/버퍼 상향 또는 설계 수정
긴 쿼리스트링 때문에 400이 나면, NGINX 레벨에서 버퍼를 늘릴 수도 있지만 API 설계를 바꾸는 게 좋습니다.
- 긴 검색 조건/필터를 GET 쿼리에 넣지 말고 POST body로 이동
- 클라이언트 상태를 URL에 과하게 담지 않기
NGINX에서 요청라인과 관련된 버퍼는 client-header-buffer-size(초기 버퍼)나 위의 large-client-header-buffers와 함께 영향을 받습니다. ingress-nginx에서는 ConfigMap으로 설정합니다.
data:
client-header-buffer-size: "16k"
large-client-header-buffers: "4 32k"
(3) 업스트림 응답 헤더가 커서 생기는 문제도 구분
간혹 400이 아니라 502/503으로 보이지만, 원인은 백엔드가 너무 큰 응답 헤더(예: Set-Cookie 다수)를 내려서 프록시가 처리 못 하는 경우가 있습니다. 이때는 다음 계열을 봅니다.
proxy-buffer-sizeproxy-buffersproxy-busy-buffers-size
ingress-nginx에서는 annotation으로도 일부 조정 가능합니다.
metadata:
annotations:
nginx.ingress.kubernetes.io/proxy-buffer-size: "16k"
nginx.ingress.kubernetes.io/proxy-buffers-number: "8"
nginx.ingress.kubernetes.io/proxy-busy-buffers-size: "32k"
다만 이 영역은 “무조건 키우기”보다,
- 왜 응답 헤더가 커졌는지(쿠키 남발, 헤더에 데이터 넣기)
- 캐시/세션 전략이 적절한지 를 먼저 점검하는 게 운영 비용을 줄입니다.
5) 재현 테스트: 크기 기준을 수치로 확인하기
(1) 413 재현: 큰 바디 보내기
# 60MB 파일 생성
dd if=/dev/zero of=/tmp/60m.bin bs=1m count=60
# 업로드 시도
curl -i https://api.example.com/upload \
-H 'Expect:' \
-F 'file=@/tmp/60m.bin'
Expect:를 비워100-continue협상 때문에 헷갈리는 상황을 줄입니다.
(2) 400(헤더) 재현: 큰 쿠키 넣기
python - <<'PY'
print('a'*40000)
PY
출력값을 쿠키로 넣어 테스트(실서비스에는 절대 금지):
BIG=$(python - <<'PY'
print('a'*40000)
PY
)
curl -i https://api.example.com/ \
-H "Cookie: big=$BIG"
이때 400이 나고 ingress 로그에 client sent too large request header가 찍히면, 헤더 버퍼 튜닝의 적중률이 높습니다.
6) 운영에서의 권장 접근 순서(실전 체크리스트)
- Ingress 로그로 원인 문자열 확정: body인지 header인지, upstream인지
- 413이면 Ingress annotation의
proxy-body-size를 경로/서비스 단위로 우선 상향 - 400이면 로그에 따라
too large request header→large-client-header-buffers조정 + 쿠키/JWT 축소too long URI→ API 설계 수정(POST로 이동) + 필요 시 버퍼 상향
- “전역 ConfigMap 변경”은 마지막에, 변경 후에는 롤아웃/재기동이 필요한지(컨트롤러 버전별 동작) 확인
- 프록시 버퍼를 키웠다면 메모리 사용량/워커 수/Pod 리소스를 함께 모니터링
7) 자주 하는 실수와 함정
(1) 모든 걸 무제한(0)으로 풀기
급한 불은 끄지만, 악성 트래픽/실수로 대용량 업로드가 들어오면 Ingress가 먼저 흔들립니다. 서비스별 상향 + 애플리케이션 레벨 제한(예: 업로드 크기 제한 응답)을 병행하세요.
(2) 400을 “백엔드 버그”로만 보는 것
Ingress에서 400이 떨어지면 백엔드 로그에는 아무것도 남지 않습니다. 이때 백엔드만 디버깅하면 시간만 날아갑니다.
(3) gRPC/스트리밍을 HTTP 업로드처럼 튜닝
gRPC는 메시지 크기 제한이 NGINX/Envoy/애플리케이션에 각각 존재합니다. 413이 아니라 502로 보이는 경우도 있어요. gRPC 최대 메시지 쪽 이슈라면 아래 글이 더 직접적입니다.
8) 마무리: 결론은 “크기 제한은 계층별로, 최소 범위로”
EKS에서 NGINX Ingress의 400/413은 대부분 요청 바디 크기(proxy-body-size) 또는 요청 헤더/요청라인 버퍼(large-client-header-buffers 등) 로 정리됩니다. 핵심은 다음 두 가지입니다.
- 413은
proxy-body-size로, 가능하면 Ingress annotation으로 서비스 단위 조정 - 400은 로그 힌트에 따라 헤더/URI 설계 개선을 우선하고, 필요 시 버퍼를 제한적으로 상향
이 원칙대로 접근하면 “전역 설정을 크게 풀고도 계속 불안한 상태”를 피하면서, 재현 가능한 방식으로 400/413을 안정적으로 줄일 수 있습니다.