HTTPQUERY
한국어

408 Request Timeout

사용법

408 Request Timeout 오류가 도착하는 경우, 클라이언트가 요청을 시작했지만 어떤 이유로 페이로드 전체를 전송하지 못한 상황입니다. 이는 인터넷 연결이 느리거나 끊겼을 때 발생합니다. 응답에는 연결이 닫혔음을 명시하는 Connection 헤더가 포함됩니다.

Connection: close 헤더를 받으면 클라이언트는 요청을 다시 시도합니다.

참고

408 Request Timeout은 클라이언트가 서버로 요청을 보내는 속도가 너무 느렸다는 뜻입니다. 504 Gateway Timeout은 프록시나 게이트웨이가 백엔드 서버의 응답을 기다리다 시간이 초과되었다는 뜻입니다. 408은 클라이언트에서 서버로의 타임아웃이고, 504는 서버에서 서버로의 타임아웃입니다. 408은 원인이 대개 일시적이므로 자동 재시도가 정당화되는 몇 안 되는 4xx 코드 중 하나입니다.

SEO 영향

Google 같은 검색 엔진은 408 상태를 반환하는 URL을 색인하지 않습니다. 이전에 색인되었다가 이 상태 코드를 반환하는 URL은 검색 결과에서 제거됩니다.

예시

클라이언트가 10K 크기의 PDF 파일을 서버로 보내기 시작하지만, 연결이 간헐적으로 끊기는 문제를 겪어 서버는 전송이 너무 느리다고 판단합니다. 서버는 요청을 취소하고 연결을 닫습니다. 연결이 안정되면 클라이언트는 파일을 성공적으로 다시 전송합니다.

최초 요청

PUT /docs HTTP/1.1
Host: example.com
Content-Type: application/pdf
Content-Length: 10000

최초 응답

HTTP/1.1 408 Request Timeout
Connection: Close
Content-Type: text/html
Content-Length: 198

<html>
  <head>
    <title>Connection Close</title>
  </head>
  <body>
    <p>The transmission was not received quickly
    enough. Check connectivity and try again.</p>
  </body>
</html>

다음 요청

PUT /docs HTTP/1.1
Host: example.com
Content-Type: application/pdf
Content-Length: 10000

<File transfer successful for PDF file>

최종 응답

HTTP/1.1 200 OK

해결 방법

408 Request Timeout은 클라이언트가 제시간에 요청을 완료하지 못해 서버가 연결을 닫았다는 뜻입니다.

네트워크 연결 상태와 지연 시간을 확인하세요. 불안정하거나 느린 연결은 요청 전송을 지연시킵니다. 서버로 ping과 traceroute를 실행해 패킷 손실이나 지연이 큰 구간을 찾아내세요. 재시도 전에 연결이 안정적인지 확인하세요.

페이로드 크기를 줄이거나 큰 업로드를 분할하세요. 느린 연결로 큰 파일을 보내면 서버의 인내심을 넘어섭니다. 페이로드를 압축하거나, 청크 방식 Transfer-Encoding을 사용하거나, 멀티파트 업로드 API로 업로드를 더 작은 조각으로 나누세요.

클라이언트 측 타임아웃 설정을 늘리세요. HTTP 클라이언트 라이브러리나 브라우저는 자체 타임아웃을 적용합니다. 느린 전송을 감당할 수 있도록 연결 및 읽기 타임아웃 값을 올리세요. curl에서는 —connect-timeout과 —max-time을 사용합니다. Python requests에서는 timeout=(connect, read)를 설정합니다.

서버의 타임아웃 설정을 확인하세요. 웹 서버에 따라 타임아웃 지시문을 조정하세요.

nginx:

client_header_timeout 60s;
client_body_timeout 60s;
keepalive_timeout 75s;
send_timeout 60s;

Apache:

Timeout 60
KeepAliveTimeout 5
RequestReadTimeout header=20-40 body=20

RequestReadTimeout 지시문(mod_reqtimeout)은 헤더와 본문의 읽기 타임아웃을 각각 제어합니다. 클라이언트가 정당하게 더 많은 시간이 필요한 경우 이 값들을 늘리세요.

Keep-Alive 설정을 조정하세요. keepalive_timeout이 낮으면 지속 연결에서 클라이언트가 다음 요청을 보내기 전에 유휴 연결이 닫힙니다. nginx의 기본 keepalive_timeout은 75초입니다. 요청이 몰렸다 끊겼다 하는 패턴의 클라이언트를 받는 API라면 이 값을 올리세요. Apache에서는 KeepAliveTimeout(기본 5초)을 늘리고, MaxKeepAliveRequests가 연결당 충분한 요청 수를 허용하는지 확인하세요.

리버스 프록시와 로드 밸런서의 타임아웃을 점검하세요. 애플리케이션 서버 앞에 리버스 프록시(nginx, HAProxy, AWS ELB)가 있으면 각 계층이 자체 타임아웃을 적용합니다. 프록시의 타임아웃이 백엔드보다 짧으면 프록시가 먼저 연결을 닫습니다. nginx 리버스 프록시 모드에서는 다음과 같습니다.

proxy_connect_timeout 60s;
proxy_read_timeout 60s;
proxy_send_timeout 60s;

AWS ALB의 기본 유휴 타임아웃은 60초이며, 로드 밸런서 속성에서 설정할 수 있습니다.

요청을 재시도하세요. 408은 일시적인 경우가 많습니다. 연결을 다시 맺고 전체 요청을 재전송하세요. 프로그램 방식의 클라이언트에는 지수 백오프를 적용한 자동 재시도 로직을 구현하세요. Retry-After 헤더가 있다면 재시도 전에 얼마나 기다려야 하는지 알려 줍니다.

서버 오류 로그를 검토하세요. nginx는 타임아웃 이벤트를 오류 로그에 기록합니다. Apache는 mod_reqtimeout 위반을 기록합니다. /var/log/nginx/error.log나 /var/log/apache2/error.log에서 요청 시각과 일치하는 항목을 찾아 헤더 읽기와 본문 읽기 중 무엇이 타임아웃되었는지 파악하세요.

코드 참고

.NET

HttpStatusCode.RequestTimeout

Rust

http::StatusCode::REQUEST_TIMEOUT

Rails

:request_timeout

Go

http.StatusRequestTimeout

Symfony

Response::HTTP_REQUEST_TIMEOUT

Python3.5+

http.HTTPStatus.REQUEST_TIMEOUT

Java

java.net.HttpURLConnection.HTTP_CLIENT_TIMEOUT

Apache HttpComponents Core

org.apache.hc.core5.http.HttpStatus.SC_REQUEST_TIMEOUT

Angular

@angular/common/http/HttpStatusCode.RequestTimeout