조회수: 99

408 Request Timeout 원인과 해결

408 Request Timeout은 서버가 응답이 아니라 요청을 기다리다 끊은 것입니다. 느린 클라이언트와 회수된 keepalive를 구분합니다. 무료 즉시 진단으로 바로 확인.

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

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

Problem

요청이 408 Request Timeout을 반환하거나, 요청 도중 연결이 끊기며 어딘가에서 408이 기록됩니다. 서버는 요청의 일부는 받았지만, 전체가 도착하기 전에 포기했습니다.

Symptoms

  • HTTP 진단의 최종 상태 코드가 408이거나, 연결이 도중에 리셋됩니다.
  • 대용량 업로드, 느린 모바일 연결, 큰 본문을 가진 요청에서 발생하고 — 작은 GET에서는 드뭅니다.
  • nginx나 로드밸런서 로그에 아무도 신고하지 않은 408이 가득합니다.
  • 같은 엔드포인트가 빠른 네트워크에서는 멀쩡하고 불안정한 네트워크에서 실패합니다.

What 408 Actually Means

RFC 9110(§15.5.9)은 408을 서버가 “기다릴 준비가 된 시간 안에 완전한 요청 메시지를 받지 못해 연결을 닫기로 결정한” 것으로 정의합니다. 두 번 읽어야 합니다. 방향이 전부니까요. 408은 요청이 늦게 도착하는 문제 — 응답이 늦게 나가는 문제가 아닙니다.

이건 대부분이 짐작하는 그 타임아웃의 정반대입니다. 504 Gateway Timeout은 프록시가 상위 서버의 응답을 너무 오래 기다릴 때 발생합니다. 524(Cloudflare)는 엣지에서의 같은 개념이고요. 하지만 408은 서버당신을 너무 오래 기다릴 때 발생합니다 — 요청 라인, 헤더, 본문의 바이트가 너무 느리게 들어오거나 아예 멈춘 것이죠. 문제는 뒤에서 도는 애플리케이션 로직이 아니라, 전송하는 쪽 또는 당신과 서버 사이 네트워크에 있습니다.

로그에서 408을 본 사람 절반을 넘어뜨리는 두 번째 반전이 있습니다. nginx를 예로 들면, client_header_timeout이나 client_body_timeout이 만료될 때 nginx는 access 로그에 408을 기록한 뒤 상태 줄을 쓰지 않고 그냥 TCP 연결을 닫습니다. 앞단의 브라우저·프록시·CDN은 그저 조용해지다가 끊긴 연결만 봅니다. 아무도 408 페이지를 렌더링하지 않았습니다. 그래서 “408이 수천 건”은 자주 “유휴 keepalive 연결 수천 개가 타임아웃되어 회수됐다”는 뜻이고, 이건 장애가 아니라 정상적인 청소입니다.

Top 3 Causes

  1. 회수된 유휴 keepalive 연결 - 클라이언트가 keepalive 연결을 열고 keepalive_timeout을 넘겨 유휴 상태로 있다가 서버가 닫은 경우입니다. 클라이언트가 그 죽은 소켓을 새 요청에 재사용하려 하거나 서버가 회수를 408로 기록하면, 사용자가 겪은 적 없는 408이 남습니다. nginx 로그에 나오는 “유령” 408의 가장 흔한 출처입니다.
  2. 진짜로 느린 클라이언트나 업로드 - 약한 모바일 링크로 올리는 대용량 파일, 멈춘 멀티파트 업로드, 연결만 열어두고 헤더 전송을 미적거리는 클라이언트. 요청 본문이 client_body_timeout이 허용하는 것보다 느리게 들어와 서버가 포기합니다. 나쁜 네트워크의 실제 사용자가 여기 걸립니다.
  3. 잘못 표기된 중간 노드의 유휴 타임아웃 - 로드밸런서와 CDN은 각자의 유휴 타임아웃을 강제합니다(AWS ALB 기본값 60초). 유휴로 연결이 끊길 때, 이를 감지한 계층이 애플리케이션 로직이 하나도 돌지 않았는데도 408로 표면화할 수 있습니다. 롱폴링과 느린 스트리밍 요청이 흔한 피해자입니다.

Diagnose with DechoNet

  • HTTP 진단으로 그 엔드포인트가 정상적인 빠른 요청에도 정말 408을 반환하는지, 아니면 깨끗하게 응답하는지 확인하세요. 우리 쪽의 빠른 자동 요청은 성공하는데 사용자만 408을 본다면, 타임아웃은 그들의 업로드 속도나 네트워크 경로에 묶인 것 — 망가진 서버가 아닙니다. 그 하나의 갈림만으로 어느 끝단을 고쳐야 하는지 알 수 있습니다.

Resolution Checklist

  • 재현하세요. 빠른 연결에서 HTTP 진단과 curl -v를 실행합니다. 여기서 깨끗한 200이 나오면 서버는 건강하고 408은 클라이언트나 네트워크 고유의 문제입니다.
  • 유령 408과 진짜 408을 분리하세요. 408이 nginx access 로그에만 있고 아무 사용자도 깨진 페이지를 신고하지 않는다면, 거의 확실히 회수된 keepalive입니다 — 튜닝하되 당황하지 마세요.
  • 어떤 요청이 실패하는지 보세요. 업로드와 큰 본문이라면 요청 본문이 너무 느리게 도착하는 것이니, client_body_timeout(nginx)이나 그에 상응하는 값을 올리고 업로드 크기 한도가 겹쳐 악화시키지 않는지 확인하세요.
  • 오리진뿐 아니라 경로의 모든 타임아웃을 점검하세요. nginx의 keepalive_timeoutclient_header_timeout, 그리고 앞단 로드밸런서·CDN의 유휴 타임아웃까지. 가장 짧은 값이 이깁니다.
  • 재시도한다면 안전하게 하세요. 408은 아무것도 처리되지 않았으니 다시 보내도 되지만, 결제나 쓰기 경로에 자동 재시도를 넣기 전에 요청이 멱등한지 확인하세요.
  • 504와 408을 가리세요. 지연이 서버가 응답하는 데 너무 오래 걸리는 것이라면 당신은 504를 쫓고 있고, 해결책은 클라이언트 타임아웃이 아니라 애플리케이션이나 상위 서버에 있습니다.

When to Escalate

  • 정상 연결의 실제 사용자가 작은 요청에서 408을 받는다면, 리버스 프록시나 로드밸런서를 관리하는 쪽에 에스컬레이션하세요 — 유휴 타임아웃이 트래픽 패턴에 비해 너무 공격적으로 설정된 것입니다.
  • 408이 트래픽 급증과 함께 치솟으면 Slowloris 유형을 의심하세요: 수많은 연결을 열고 소켓을 붙잡을 만큼만 딱 느리게 바이트를 흘려보내는 패턴입니다. 이건 설정 버그가 아니라 서비스 거부 형태이고, 엣지 방어를 운영하는 쪽의 몫입니다.

관련 도구

관련 가이드

가이드 공유

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