앞단이 내는 코드는 어디가 문제인지를 꽤 정확히 가리킨다.
| 코드 | 누가 냈나 | 흔한 원인 |
|---|---|---|
| 502 Bad Gateway | nginx | 업스트림이 죽었거나 응답이 깨졌다. 포트 오지정 |
| 504 Gateway Timeout | nginx | proxy_read_timeout 안에 응답을 못 했다 |
| 499 | nginx 전용 | 클라이언트가 먼저 끊었다 — 기다리다 포기 |
| 413 | nginx | client_max_body_size 초과 |
| 400 | nginx | 헤더가 너무 크거나 요청이 깨졌다 |
| 404 | 대개 업스트림 | proxy_pass 슬래시로 경로가 어긋난 경우가 많다 |
502 와 504 를 먼저 가른다
-
502 — 연결은 시도했는데 실패했다
- 프로세스가 떠 있는가 · 포트가 맞는가 · 업스트림 헬스
- keepalive 설정이 반쪽이면 재사용하려던 연결이 이미 끊겨 502 가 난다
-
504 — 연결은 됐는데 늦다
- 애플리케이션 · DB · 타임아웃 값
- upstream_response_time 을 보면 바로 확인된다
499 를 놓치지 않는다
499 는 nginx 가 만든 비표준 코드다 — '클라이언트가 먼저 끊었다'
499 가 늘어난다 = 우리가 느려졌다는 뜻이다
- 클라이언트 타임아웃보다 서버가 느려서 기다리다 포기한 것
5xx 만 보고 있으면 이 신호를 놓친다
- 서버 입장에서는 에러가 아니므로 에러율 대시보드에 안 잡힌다
- 사용자 입장에서는 명백한 실패다
로그로 앞·뒤를 가른다
log_format upstream_time '$remote_addr "$request" $status '
'rt=$request_time uct=$upstream_connect_time '
'uht=$upstream_header_time urt=$upstream_response_time '
'ua=$upstream_addr us=$upstream_status';
-
rt — nginx 가 요청을 받아 응답을 다 보낼 때까지 (클라이언트 전송 포함)
-
uct — 업스트림에 연결하는 데 걸린 시간
-
uht — 업스트림이 첫 헤더를 줄 때까지 (≈ 서버 처리 시간)
-
urt — 업스트림 응답을 다 받을 때까지
-
rt 는 큰데 urt 가 작다 — → 뒤는 빠르다. 느린 클라이언트이거나 앞단 문제
-
urt 가 크다 — → 애플리케이션이 느리다
-
uct 가 크다 — → 연결 자체가 안 된다. 포화 · 네트워크 · 풀 고갈
-
ua 가 특정 서버에 몰린다 — → 그 인스턴스만 느리거나 분배가 치우쳤다
-
us 와 status 가 다르다 — → nginx 가 만든 코드다 (502 · 504)
이 한 줄짜리 로그 포맷이 "앞이냐 뒤냐" 를 가르는 가장 싼 도구다. 트랙을 열 때 가장 먼저 넣는다.
상태 코드 분포를 본다
awk '{print $9}' access.log | sort | uniq -c | sort -rn
평소 분포를 알고 있어야 이상이 보인다
· 404 가 갑자기 는다 → 배포로 경로가 바뀌었거나 스캔 트래픽
· 499 가 는다 → 우리가 느려졌다
· 502/504 가 는다 → 뒤가 아프다
사고 유형별 첫 확인 지점
413 이 난다
- 계층마다 한도가 있다 — CDN → LB → nginx → WAS
- 하나만 낮아도 거기서 막힌다. 응답의 Server 헤더로 어느 계층인지 가른다
https 인데 http 로 리다이렉트된다
- X-Forwarded-Proto 누락. curl -I 로 Location 헤더를 본다
배포했는데 옛 화면이 보인다
- HTML 이 캐시됐다. 응답의 Cache-Control · Age 헤더 확인
- CDN 이면 엣지 무효화가 필요하다
특정 시간대에만 502
- upstream keepalive 미설정 → 포트 고갈
- 또는 배포 시각과 겹친다 → graceful shutdown 순서
디스크가 찼는데 로그 파일은 작다
- 로그 회전 후 SIGUSR1 미전송. lsof | grep deleted 로 확인
진단 명령
nginx -T # include 를 펼친 최종 설정
ss -lnt # LISTEN · Recv-Q(대기) · Send-Q(백로그)
ss -s # TIME_WAIT 등 소켓 요약
lsof | grep deleted # 삭제됐는데 열려 있는 파일
curl -w 'dns=%{time_namelookup} conn=%{time_connect} tls=%{time_appconnect} \
ttfb=%{time_starttransfer} total=%{time_total}\n' -o /dev/null -s https://example.com
마지막 curl -w 한 줄이 DNS·TCP·TLS·서버 처리·전송을 분리해 보여 준다. 어느 구간이 느린지를 가장 빠르게 가른다.
면접 함정
- ❌ "502와 504는 둘 다 백엔드 문제" → 502는 연결, 504는 지연으로 조치가 다르다.
- ❌ "499는 무시해도 된다" → 사용자가 포기한 요청이다. 가장 중요한 신호일 수 있다.