이 문서를 통해 아래 질문들에 답할 수 있게 됩니다.
- NGINX timeout이 connect, send, read, client body 단계별로 어떻게 다르게 동작하는가?
- buffering이 upload, SSE, streaming, gRPC에서 어떤 장애를 만드는가?
- proxy timeout/body limit을 앱 timeout과 어떻게 맞춰야 하는가?
개요
NGINX timeout과 buffering은 사용자 경험과 backend 보호 사이의 조절 장치다. 앱이 정상 처리 중이어도 proxy가 먼저 끊으면 사용자는 504를 보고, client upload가 proxy에서 막히면 앱 로그 없이 413이 발생한다. streaming endpoint는 buffering 설정 하나로 “실시간”이 아니라 “모아서 한 번에” 보내지는 문제가 생긴다.
proxy_connect_timeout은 upstream 연결을 맺는 시간 제한이다.proxy_read_timeout은 upstream 응답을 기다리는 시간 제한이다.client_max_body_size는 요청 body 크기를 proxy에서 제한한다.proxy_buffering은 upstream 응답을 NGINX가 버퍼링할지 결정한다.proxy_request_buffering은 client request body를 먼저 다 받은 뒤 upstream에 보낼지 결정한다.
원리
timeout은 “전체 요청 시간” 하나가 아니다.
client upload
-> NGINX receives body
-> NGINX connects upstream
-> upstream processes
-> NGINX reads response
-> NGINX sends response to client어느 구간에서 시간이 걸리는지에 따라 설정과 로그가 달라진다. upstream timed out while reading response header from upstream은 upstream 연결은 되었지만 응답 header를 제때 못 받았다는 뜻이다.
주요 설정 판단
| 설정 | 의미 | 흔한 증상 |
|---|---|---|
client_max_body_size | 허용 request body 크기 | 앱 로그 없는 413 |
client_body_timeout | client body 수신 대기 | 느린 업로드 중 끊김 |
proxy_connect_timeout | upstream 연결 대기 | 502/504, connect timeout |
proxy_send_timeout | upstream으로 request 전송 대기 | 큰 body 전송 중 실패 |
proxy_read_timeout | upstream 응답 read 대기 | long API 504 |
proxy_buffering | response buffering | SSE/stream 지연 |
proxy_request_buffering | request body buffering | 대용량 upload latency/디스크 사용 |
Endpoint별 설정 예시
일반 API는 보수적인 timeout과 body limit을 둔다.
location /api/ {
proxy_pass http://app_upstream;
proxy_connect_timeout 2s;
proxy_send_timeout 10s;
proxy_read_timeout 30s;
client_max_body_size 10m;
}파일 업로드는 body limit과 request buffering을 별도로 판단한다.
location /upload/ {
proxy_pass http://app_upstream;
client_max_body_size 200m;
proxy_request_buffering off;
proxy_read_timeout 120s;
}SSE는 response buffering을 끈다.
location /events/ {
proxy_pass http://app_upstream;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_buffering off;
proxy_read_timeout 1h;
}앱 Timeout과의 정렬
proxy timeout은 app timeout, DB timeout, client timeout보다 무작정 길거나 짧으면 안 된다.
- app 처리 timeout보다 proxy read timeout이 너무 짧으면 proxy가 먼저 504를 만든다.
- proxy timeout이 너무 길면 느린 upstream에 연결이 오래 묶인다.
- retry가 있는 client라면 proxy timeout 이후 재시도가 중복 처리를 만들 수 있다.
- upload는 client timeout, proxy body timeout, app multipart limit을 모두 맞춰야 한다.
코드와 명령으로 확인하기
업로드 limit을 확인한다.
dd if=/dev/zero of=/tmp/large.bin bs=1m count=25
curl -i https://api.example.com/upload \
-F "file=@/tmp/large.bin"응답 시간 분리를 본다.
curl -o /dev/null -s -w 'connect=%{time_connect} first_byte=%{time_starttransfer} total=%{time_total} code=%{http_code}\n' \
https://api.example.com/api/slowNGINX 설정은 include까지 펼친 실제 적용본으로 확인한다.
nginx -T | grep -nE 'proxy_(connect|read|send)_timeout|client_max_body_size|proxy_buffering|proxy_request_buffering'인프라 협업 포인트
- 앱 팀은 endpoint별 예상 처리 시간, body 크기, streaming 여부, idempotency 여부를 알려야 한다.
- 인프라 팀은 proxy timeout, LB idle timeout, gateway timeout, CDN timeout의 전체 체인을 공유해야 한다.
- 대용량 upload는 proxy 임시 디스크 사용량과 backend pressure를 함께 본다.
- timeout 변경은 SLO와 downstream 보호 정책에 영향을 주므로 장애 대응 중 임시 완화인지 장기 정책인지 구분한다.
실전 팁
- 30초를 넘는 동기 API는 UX와 retry 관점에서 다시 설계한다. job 제출 + polling/webhook이 더 나을 수 있다.
- SSE/WebSocket/gRPC는 일반 REST location과 분리해 timeout/buffering을 따로 둔다.
- 413은 앱 validation이 아니라 proxy limit일 수 있다. 사용자 메시지와 문서에 최대 크기를 맞춘다.
proxy_request_buffering off는 streaming upload에 유리하지만 upstream을 느린 client에 직접 노출한다.- 개인 프로젝트는
/api,/upload,/events정도만 분리해도 많은 장애를 피한다.
위험 신호!
- 모든 endpoint가 같은 timeout/body limit을 공유한다.
- proxy timeout은 늘렸지만 DB/query timeout은 그대로다.
- SSE endpoint에서
proxy_buffering을 확인하지 않는다. - 504가 발생해도 앱 처리 완료/중복 처리 여부를 확인하지 않는다.
확인 질문
확인 질문
- 앱 로그가 없는 413에서 먼저 볼 설정은 무엇인가?
- NGINX나 gateway의
client_max_body_size같은 request body limit을 먼저 확인한다.- SSE에서 buffering이 문제인 이유는 무엇인가?
- proxy가 작은 이벤트를 모아 두었다가 한 번에 보내면 client가 실시간 이벤트를 받지 못하기 때문이다.
- proxy timeout과 app timeout을 함께 봐야 하는 이유는 무엇인가?
- proxy가 먼저 끊으면 앱은 처리 중이어도 사용자는 실패를 보고, retry가 중복 처리를 만들 수 있기 때문이다.