조회수: 23

499 Client Closed Request 원인 진단

499 Client Closed Request는 호출자가 nginx 응답 전에 끊은 것. 사용자인지 타임아웃 짧은 로드밸런서인지 무료 즉시 진단으로 바로 확인.

내 도메인에 이 문제가 있는지 지금 확인

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

Problem

nginx 액세스 로그가 499 상태 코드로 차오릅니다. 오류 페이지를 봤다는 사용자는 없습니다 — 애초에 없으니까요. 499는 클라이언트가 nginx의 응답이 끝나기 전에 연결을 끊었다는 nginx의 기록입니다. 요청이 들어왔고, 작업이 시작됐고, 답이 준비되기 전에 상대가 떠났습니다.

Symptoms

  • nginx 액세스 로그가 겉보기엔 정상인 요청에 최종 상태 499를 남깁니다.
  • 아무도 오류를 보지 못합니다. 499 페이지가 없는 건, nginx가 응답하려 할 때 연결이 이미 닫혀 있었기 때문입니다.
  • 499가 가장 느린 엔드포인트에 몰립니다 — 리포트, 내보내기, 검색, DB나 업스트림 API를 타는 모든 것.
  • 같은 URL을 참을성 있는 클라이언트로 HTTP 진단이나 curl을 돌리면 깔끔한 200이 나옵니다. 실패는 타임아웃 상황에서만 보입니다.

499가 실제로 뜻하는 것

499는 표준 HTTP 상태 코드가 아닙니다. nginx가 발명했고, nginx 자신의 로그에만 존재합니다. 클라이언트가 nginx가 응답을 보내기 전에 TCP 연결을 닫으면 nginx는 답할 상대가 없으니 액세스 로그에 499를 쓰고 넘어갑니다. 그 상태줄은 전송되지 않습니다 — 하류의 어떤 브라우저·프록시·CDN도 499를 받지 않습니다. 이것이 499에 관해 가장 많이 오해받는 사실입니다: 499는 nginx가 누군가에게 보낸 응답이 아니라 호출자가 떠났다는 기록입니다.

그래서 499가 진짜 던지는 질문은 이것입니다: 누가 끊었고, 왜 그렇게 급했는가?

솔깃한 답은 “성질 급한 사용자가 탭을 닫았다”입니다. 가끔은 맞습니다. 하지만 요즘 스택에서 nginx가 브라우저를 직접 마주하는 일은 거의 없습니다. nginx는 로드밸런서·CDN·API 게이트웨이·인그레스 컨트롤러 뒤에 앉아 있고, 499에 찍히는 클라이언트는 바로 그것들입니다. 각각 자기 타임아웃을 강제하고, 그중 가장 짧은 것이 이깁니다. AWS ALB의 유휴 타임아웃이 60초인데 업스트림이 리포트 하나 만드는 데 75초가 걸리면, ALB는 60초에 포기하고 nginx로의 연결을 닫으며, nginx는 성공을 몇 초 앞둔 요청에 499를 남깁니다. “Client Closed Request”의 그 클라이언트는 당신의 인프라 자신이었습니다.

이 재구성이 조사 전체를 바꿉니다. 499 급증은 클라이언트 문제인 경우가 드뭅니다. 타임아웃 불일치 문제입니다: nginx 위쪽의 무언가가 느리고, nginx 앞쪽의 무언가가 그 느린 것이 끝나기 전에 인내심을 잃는 것입니다.

당신 것이 아닌 499 (460, 463, 그리고 친척들)

nginx가 AWS 뒤에 있으면 같은 사건이 다른 숫자를 달고 나타납니다. Application Load Balancer는 클라이언트가 ALB의 타깃 응답을 받기 전에 연결을 닫으면 460을 기록합니다 — ALB판 499입니다. 들어오는 X-Forwarded-For 헤더에 IP 주소가 30개를 넘으면(어떤 설정으로도 못 바꾸는 하드 리밋) 463을 기록하는데, 대개 프록시 체인이 홉을 잘못 쌓은 것입니다. HAProxy는 같은 클라이언트 중단 이야기를 종료 상태 필드에 남깁니다. 이름은 달라도 이야기는 하나입니다: 방문자와 애플리케이션 사이의 어떤 계층이 기다림을 멈추기로 한 것입니다.

499를 nginx의 return 444와 헷갈리지도 마세요. 444는 nginx가 응답 없이 연결을 일부러 닫는 것 — 대개 나쁜 봇을 떨구려고 당신이 설정한 것입니다. 499는 클라이언트가 당신에게 끊는 것입니다. 하나는 당신의 선택, 다른 하나는 남의 선택입니다.

주요 원인 3가지

  1. 업스트림 응답 시간보다 짧은 앞단 타임아웃 - 압도적으로 흔한 원인. 30~60초 타임아웃을 가진 로드밸런서·CDN·게이트웨이가, 가끔 그보다 오래 걸리는 엔드포인트 앞에 앉아 있습니다. 앞단이 끊고, nginx가 499를 남기며, 작업은 거의 끝나 있던 경우가 많습니다. 해결은 nginx가 아니라 체인 전체의 타임아웃을 맞추는 것입니다.
  2. 진짜로 느린 업스트림 - nginx가 앱 서버로 프록시하는데, 그 앱이 특정 경로에서 느린 것입니다 — N+1 쿼리, 타임아웃 없는 외부 API, 콜드 캐시. 클라이언트(사람이든 기계든)가 당연히 포기합니다. 여기서 499는 증상이고, 병은 업스트림 지연입니다. 프로파일링할 대상은 그쪽입니다.
  3. 헬스 체크와 버려진 요청 - 모니터링 프로브, 업타임 체커, 페이지를 떠나는 사용자 모두 설계상 연결을 일찍 닫습니다. 느린 경로에서 빠른 응답을 기대하는 로드밸런서 헬스 체크는 아무 의미 없는 499를 꾸준히 만듭니다. 숫자를 사건으로 취급하기 전에 이걸 실트래픽에서 분리하세요.

DechoNet으로 진단

  • 문제의 URL을 HTTP 진단하세요. 우리 쪽에서 보낸 정상 요청이 합리적인 시간에 깔끔한 200을 돌려주면 nginx와 업스트림은 건강한 것 — 499는 깨진 엔드포인트가 아니라 클라이언트 쪽 타임아웃이 만드는 것입니다. 이 갈림이 진단의 전부입니다: 해결이 nginx 설정이 아니라 타임아웃 값이나 업스트림 지연에 있다고 알려줍니다.
  • 우리 진단 느리거나 실패하면 업스트림 자체가 문제임을 확인한 것이고, 499는 성질 급한 호출자들이 그걸 보고하는 방식일 뿐입니다.

해결 체크리스트

  • 엔드포인트가 실제로 건강한지 확인. HTTP 진단과 참을성 있는 클라이언트로 curl -w '%{time_total}'를 돌리세요. 빠르고 깔끔한 200이면 nginx는 멀쩡하고 499는 타이밍 문제지 정확성 문제가 아닙니다.
  • 체인의 모든 타임아웃을 찾아 줄 세우기. CDN, 로드밸런서(ALB idle_timeout 기본 60초), API 게이트웨이, nginx proxy_read_timeout, 앱 서버. 가장 짧은 게 이깁니다 — 앞단 타임아웃을 현실적 최악 응답 시간보다 길게 하거나, 응답을 빠르게 만드세요.
  • 느린 업스트림을 프로파일링. 499가 특정 경로를 따라간다면 진짜 일은 그쪽입니다: 느린 쿼리, 타임아웃 없는 외부 호출, 캐싱 누락. 지연을 고치면 499가 원천에서 사라집니다.
  • 프로브와 사람을 분리. 헬스 체크·모니터링 UA를 태깅하거나 제외해 숫자를 부풀리지 않게 하세요. 업타임 로봇의 499는 장애가 아닙니다.
  • 연결이 끊긴 뒤에도 업스트림이 계속 일해야 하는지 결정. proxy_ignore_client_abort on은 클라이언트가 떠난 뒤에도 nginx가 업스트림 요청을 끝내게 합니다 — 백엔드가 반드시 완료돼야 할 때(결제, 쓰기) 유용하지만, 버려진 작업에 자원만 태울 땐 낭비입니다. 기본값은 off이니 의도적으로 고르세요.

언제 에스컬레이션할까

  • 499가 업스트림 지연과 함께 급증하고 실사용자가 영향받으면, 이건 로깅 특이현상이 아니라 성능 사건입니다 — 느린 서비스를 소유한 쪽으로 올리세요.
  • 499가 엔드포인트가 필요로 하는 것보다 짧은 로드밸런서·CDN 타임아웃으로 추적되면, 그 엣지 계층을 소유한 쪽으로 올리세요. 해결은 타임아웃 정책이고, nginx보다 한 홉 위에 있습니다.

관련 도구

관련 가이드

가이드 공유

[Ad] Guide Detail Inline
← 전체 가이드 보기