운영 오류, 누구에게 연락할지 먼저 판단
502 / 503 / 504 비교표가 애플리케이션이 죽었는지, 트래픽에서 빠졌는지, 너무 느린지 알려 줍니다. Cloudflare 52x 카드는 "오리진 방화벽 / 인증서 / 타임아웃을 확인하라"는 방향을 바로 제시합니다.
Guide
HTTP 상태 코드 전체 표는 72개 상태 코드를 담았습니다. RFC 9110이 정의한 표준 코드 전부(100–101, 200–206, 300–308, 400–418, 421, 422, 426, 500–505), RFC 6585 / 7725 / 8297 / 8470과 WebDAV 계열 RFC가 정의한 확장 코드(103, 207, 208, 226, 423–425, 428, 429, 431, 451, 506–511), 그리고 어떤 RFC에도 없지만 운영 장애를 추적할 때 매일 마주치는 비표준 코드인 nginx의 444와 499, Cloudflare의 520–526까지 들어 있습니다.
2026-09-09 업데이트4 sources7 min read
HTTP 상태 코드 전체 표는 72개 상태 코드를 담았습니다. RFC 9110이 정의한 표준 코드 전부(100–101, 200–206, 300–308, 400–418, 421, 422, 426, 500–505), RFC 6585 / 7725 / 8297 / 8470과 WebDAV 계열 RFC가 정의한 확장 코드(103, 207, 208, 226, 423–425, 428, 429, 431, 451, 506–511), 그리고 어떤 RFC에도 없지만 운영 장애를 추적할 때 매일 마주치는 비표준 코드인 nginx의 444와 499, Cloudflare의 520–526까지 들어 있습니다.
"의미"만 알려 주는 상태 코드 표와 달리 여기서는 코드마다 네 단락으로 씁니다. 의미가 무엇인지, 어떤 상황에서 마주치게 되는지, 프런트엔드 / 클라이언트가 받으면 무엇을 해야 하는지(재시도? 로그인으로 이동? 어느 응답 헤더를 읽어야 하나?), 그리고 백엔드가 언제 이 코드를 돌려주고 어떤 헤더를 붙여야 하는지입니다. 빈도가 높은 코드에는 "흔한 오용"이 한 단락 더 붙습니다. 운영에서 상태 코드 문제는 대개 코드를 몰라서가 아니라 잘못 써서 생기기 때문입니다. 비즈니스 실패에 200을 주고, 로그인하지 않은 사용자에게 403을 주고, 속도 제한에 503을 주는 식입니다.
페이지 맨 위에는 일곱 쌍의 "헷갈리기 쉬운 비교"도 있습니다. 401 vs 403, 301/302/307/308, 400 vs 422, 404 vs 410, 502/503/504, 200/201/202/204, 429 vs 503입니다. 면접, 코드 리뷰, 장애 대응에서 가장 자주 질문받는 조합들입니다.
502)를 치면 정확히 일치하는 카드가 맨 위에 고정됩니다."429"를 검색하면 나오는 카드 내용(발췌):
대응하는 백엔드 응답은 이렇게 생겼습니다:
429 Too Many Requests 요청 과다 RFC 6585
의미 주어진 시간 안에 요청을 너무 많이 보냄(속도 제한).
시점 API 호출 제한, 로그인 시도 과다, 크롤러 속도 제한, AI API의 분당 할당량 초과.
프런트 Retry-After 헤더(초 또는 날짜)를 읽고 지수 백오프로 재시도; 사용자에게 "잠시 후 다시 시도" 안내; 즉시 재시도하지 말 것.
백엔드 Retry-After와 RateLimit-* 헤더를 붙이고 사용자 / IP / API 키 단위로 집계.
오용 속도 제한에 403이나 503을 반환하면 클라이언트가 권한 문제인지 잠시 기다려야 하는지 구분할 수 없음.HTTP/1.1 429 Too Many Requests
Content-Type: application/json
Retry-After: 30
RateLimit-Limit: 60
RateLimit-Remaining: 0
RateLimit-Reset: 30
{"error":"rate_limited","message":"분당 최대 60회 요청, 30초 후 다시 시도하세요"}상태 코드의 첫 자리가 분류를 정하는 것은 의도된 설계입니다. 클라이언트가 특정 코드(예를 들어 새로 생긴 425)를 모르더라도 분류만으로 올바르게 처리할 수 있습니다. 4xx는 한결같이 "그대로 재시도하지 말 것, 문제는 요청에 있음", 5xx는 "나중에 재시도해도 됨", 3xx는 "Location을 볼 것"입니다. RFC 9110은 클라이언트가 분류 의미는 반드시 이해해야 하지만 개별 코드는 몰라도 된다고 분명히 밝힙니다. 사용자 정의 상태 코드도 올바른 백 단위 구간에 넣어야 하는 이유가 여기 있습니다.
역사적인 작명 실수입니다. 초기 HTTP는 authentication(당신이 누구인지)과 authorization(무엇을 할 수 있는지)을 구분하지 않아 401에 Unauthorized라는 이름이 붙었지만, 짝을 이루는 WWW-Authenticate 헤더와 "자격 증명을 다시 제시하면 성공할 수 있다"는 의미는 모두 인증을 가리킵니다. RFC 9110 §15.5.2는 표현을 "lacks valid authentication credentials"로 바꿨습니다. 한 줄로 기억하세요. 401 = 로그인하세요, 403 = 로그인해도 안 됩니다. 프런트엔드는 이걸 보고 로그인 페이지로 보낼지 "권한 없음"을 표시할지 정하는데, 반대로 쓰면 "로그인했는데도 로그인 페이지로 계속 튕기는" 상황이 생깁니다.
자주 쓰는 리다이렉트 코드 넷은 사실 2×2 행렬입니다. 301과 308은 영구(브라우저와 검색 엔진이 새 주소를 기억), 302와 307은 임시입니다. 역사적으로 브라우저는 301/302를 처리할 때 POST를 GET으로 바꿨고, RFC 9110은 이 현실을 인정하면서 "메서드와 요청 본문을 반드시 유지"하는 엄격한 버전으로 307/308을 제공합니다. 웹사이트의 https 이동에 301을 쓰는 건 문제없지만, API 엔드포인트를 이전할 때 POST 요청은 307/308을 써야 합니다. 그러지 않으면 요청 본문이 사라집니다.
304는 흔히 "요청 한 번을 아꼈다"로 오해받습니다. 실제로는 브라우저가 If-None-Match / If-Modified-Since를 붙인 조건부 요청을 서버에 보냈고, 서버가 비교한 뒤 응답 본문 없이 304를 돌려준 것입니다. 아낀 것은 전송이지 왕복이 아닙니다. 요청 자체를 아끼는 일은 Cache-Control: max-age가 맡습니다(유효 기간 안에서는 브라우저가 서버에 아예 묻지 않습니다). 둘은 함께 씁니다. max-age가 살아 있는 동안에는 응답을 그대로 쓰고, 만료되면 조건부 요청으로 304를 받아 갱신합니다. DevTools에 "200 (from disk cache)"가 뜨면 전자, "304"가 뜨면 후자입니다.
운영에서 가장 흔한 5xx 조합이고, 이들을 구분하면 조사 범위가 곧바로 좁혀집니다. 리버스 프록시(Nginx, 게이트웨이, CDN)가 업스트림에 연결하지 못했거나 업스트림이 잘못된 응답을 준 경우가 502이니 애플리케이션 프로세스가 죽었는지 보세요. 업스트림이 바쁘거나 점검 중이라고 명확히 알린 경우가 503이고 보통 능동적으로 반환하니 헬스 체크와 서킷 브레이커를 보세요. 업스트림이 한참 응답하지 않는 경우가 504이니 슬로우 쿼리와 타임아웃 설정을 보세요. Cloudflare는 이 세 경우를 520–526으로 더 나눕니다. 521 ≈ 502(연결 거부), 522/524 ≈ 504(연결 / 읽기 시간 초과), 525/526은 TLS 계층 문제입니다.
가장 흔한 안티패턴은 모든 요청에 200을 돌려주고 오류를 응답 본문의 code 필드에 담는 것입니다. 그러면 CDN이 오류 페이지를 캐시하고, 모니터링의 5xx 경보가 무력해지고, 브라우저의 fetch().ok가 의미를 잃고, 재시도 전략도 세울 수 없습니다. 올바른 분업은 HTTP 상태 코드가 "요청 계층에서 무슨 일이 있었는지"(RFC 9110의 의미)를 표현하고, 본문의 비즈니스 코드가 "비즈니스 계층의 세부 사항"을 표현하는 것입니다. 둘은 서로를 대체하지 않고 역할을 나눕니다. 재고 부족은 200 + {"code":500}이 아니라 422 + {"code":"OUT_OF_STOCK"}입니다.
502 / 503 / 504 비교표가 애플리케이션이 죽었는지, 트래픽에서 빠졌는지, 너무 느린지 알려 줍니다. Cloudflare 52x 카드는 "오리진 방화벽 / 인증서 / 타임아웃을 확인하라"는 방향을 바로 제시합니다.
카드의 "프런트 / 클라이언트" 칸을 보고 인터셉터를 작성합니다. 401은 토큰을 갱신하거나 로그인으로 보내고, 403은 권한 없음을 알리고, 429는 Retry-After를 읽어 물러서며, 5xx는 제한적으로 재시도하고 보고합니다.
리소스 생성이 201 + Location을 돌려주나요? 삭제가 204인가요? 비즈니스 검증 실패가 400인가요 422인가요? 속도 제한에 Retry-After가 붙어 있나요? "백엔드" 칸과 "흔한 오용" 칸이 곧 리뷰 체크리스트입니다.
일곱 쌍의 비교표가 가장 흔한 "차이" 유형 질문을 덮고, "동작 원리"가 401의 작명, 307/308이 생긴 이유, 304의 실제 역할을 설명합니다.
사이트의 진입 서버(게이트웨이)가 요청을 실제로 처리하는 백엔드에 연결하지 못한 상태로, 보통 백엔드 프로세스가 죽었거나 재시작 중일 때 나옵니다. 사용자는 나중에 새로 고침하는 것 말고 할 수 있는 게 없습니다. 내 사이트라면 백엔드 프로세스와 게이트웨이 오류 로그를 보세요.
클라이언트 쪽은 Retry-After 헤더를 읽고 그만큼 기다린 뒤 재시도하며, 여러 번 실패하면 지수 백오프(1s, 2s, 4s…)로 물러서고 사용자에게 "너무 자주 시도했습니다"를 알립니다. 서버 쪽은 429를 반환할 때 반드시 Retry-After를 붙이고, 가능하면 RateLimit-* 헤더도 함께 보내 클라이언트가 미리 속도를 줄일 수 있게 합니다.
요청한 대상에 따라 다릅니다. GET /users/123에서 123이 없으면 404이고, GET /users?name=xxx의 결과가 비었으면 200 + 빈 배열입니다. 목록 API의 빈 결과는 "결과가 있고, 그 결과가 비었다"는 정당한 응답이지 오류가 아닙니다.
204에는 응답 본문이 없고, json()이 빈 문자열을 파싱하면 SyntaxError를 던집니다. 먼저 response.status === 204(또는 response.headers.get('content-length') === '0')를 확인하고 파싱 여부를 정하세요.
둘은 nginx가 액세스 로그에 남기는 내부 코드입니다. 444는 nginx가 아무 응답도 보내지 않고 연결을 닫았다는 뜻이고, 499는 nginx가 응답하기 전에 클라이언트가 끊었다는 뜻입니다. 브라우저에서는 "연결이 재설정됨"이 보이거나 아무 응답도 받지 못합니다.
상태 코드 데이터는 페이지와 함께 정적으로 내려받고, 검색, 필터, 복사, 즐겨찾기는 모두 브라우저 안에서 처리되며 아무 요청도 보내지 않습니다. 즐겨찾기는 이 기기의 localStorage에 저장됩니다. 외부 링크는 RFC Editor, IANA, Cloudflare 문서를 가리키며 클릭하면 해당 사이트가 처리합니다.
2026-09-09 업데이트
RFC 9110 전체 코드 + 확장 코드 + nginx/Cloudflare 비표준 코드 총 72개. 각 코드의 의미, 발생 상황, 프런트엔드/백엔드 처리, 흔한 오용을 수록. 혼동하기 쉬운 7쌍 비교 포함. 검색·복사·즐겨찾기 지원
이 도구는 아직 완전히 번역되지 않아 일부 내용이 원문 또는 대체 언어로 표시됩니다.
요청이 수신되어 처리가 계속되고 있습니다. 브라우저가 페이지 스크립트에 이 상태를 노출하는 경우는 거의 없습니다.
요청 헤더를 받았습니다. 클라이언트는 요청 본문을 계속 보낼 수 있습니다.
RFC 9110 §15.2.1
서버가 Upgrade 헤더를 통한 프로토콜 전환에 동의했습니다.
RFC 9110 §15.2.2
서버가 요청을 받아 처리 중이지만 최종 응답은 아직 없습니다.
RFC 2518(WebDAV, 폐지)
최종 응답 전에 일부 헤더(주로 Link: preload)를 먼저 보내 브라우저가 리소스를 일찍 불러오게 합니다.
RFC 8297
요청이 정상적으로 수신·이해되어 처리되었습니다.
요청이 성공했고 본문에 요청한 리소스 또는 작업 결과가 담겨 있습니다.
RFC 9110 §15.3.1
요청이 성공했고 새 리소스가 생성되었습니다.
RFC 9110 §15.3.2
요청은 수락되었지만 아직 처리되지 않았습니다. 비동기 작업의 접수증입니다.
RFC 9110 §15.3.3
응답은 성공이지만 내용이 중간 프록시에 의해 수정되었습니다.
RFC 9110 §15.3.4
성공했지만 응답 본문이 없습니다.
RFC 9110 §15.3.5
성공했고 클라이언트가 문서 보기를 재설정(예: 폼 비우기)해야 합니다.
RFC 9110 §15.3.6
리소스의 일부만 반환합니다(Range 요청 성공).
RFC 9110 §15.3.7
본문이 XML이고 각 하위 리소스가 각자의 상태를 보고합니다.
RFC 4918(WebDAV)
207 응답 안에서 같은 리소스가 앞서 보고되었으므로 반복하지 않습니다.
RFC 5842(WebDAV)
서버가 현재 인스턴스에 인스턴스 조작(델타 인코딩)을 적용해 차이를 반환했습니다.
RFC 3229
요청이 완료되려면 클라이언트가 추가 동작(보통 다른 주소로 다시 요청)을 해야 합니다.
리소스에 여러 표현이 있어 클라이언트가 하나를 골라야 합니다.
RFC 9110 §15.4.1
리소스가 Location의 새 URL로 영구 이동했습니다. 앞으로는 새 URL을 사용합니다.
RFC 9110 §15.4.2
리소스가 일시적으로 다른 곳에 있습니다. 다음에도 원래 URL을 요청합니다.
RFC 9110 §15.4.3
이 요청의 메서드와 무관하게 GET으로 Location에서 결과를 가져옵니다.
RFC 9110 §15.4.4
리소스가 변경되지 않았습니다. 캐시에 있는 복사본을 사용하세요.
RFC 9110 §15.4.5
Location에 지정된 프록시를 통해서만 리소스에 접근할 수 있습니다.
RFC 9110 §15.4.6(폐기)
초안에서 프록시 전환을 의미했지만 지금은 예약되어 사용되지 않습니다.
RFC 9110 §15.4.7
임시 리다이렉트이며 클라이언트는 같은 메서드와 본문으로 다시 보내야 합니다.
RFC 9110 §15.4.8
메서드와 본문도 유지하는 영구 리다이렉트(301의 엄격 버전)입니다.
RFC 9110 §15.4.9
요청 자체에 문제가 있습니다. 구문, 인증, 없는 리소스, 과도한 빈도 등이며 같은 요청을 다시 보내도 성공하지 않습니다.
서버가 요청을 이해할 수 없습니다. 구문 오류, 파라미터 누락이나 형식 오류, JSON 파싱 실패 등입니다.
RFC 9110 §15.5.1
자격 증명이 없거나 유효하지 않습니다 — "당신은 누구입니까?"(이름은 Unauthorized지만 실제 의미는 미인증입니다).
RFC 9110 §15.5.2
미래의 디지털 결제를 위해 예약되어 있고 명세는 구체적 의미를 정의하지 않습니다.
RFC 9110 §15.5.3(예약)
서버는 당신이 누구인지 알지만 권한이 없습니다 — "할 수 없습니다". 다시 인증해도 소용없습니다.
RFC 9110 §15.5.4
서버가 요청한 리소스를 찾지 못했고 일시적인지 영구적인지도 알려주지 않습니다.
RFC 9110 §15.5.5
리소스는 존재하지만 이 HTTP 메서드를 지원하지 않습니다.
RFC 9110 §15.5.6
Accept / Accept-Language 요구를 만족하는 표현이 서버에 없습니다.
RFC 9110 §15.5.7
먼저 프록시 서버에 인증해야 합니다(401의 프록시 버전).
RFC 9110 §15.5.8
서버가 요청을 너무 오래 기다렸습니다(클라이언트가 끝까지 보내지 않았습니다).
RFC 9110 §15.5.9
요청이 리소스의 현재 상태와 충돌합니다. 요청을 바꾸면 해결될 수 있습니다.
RFC 9110 §15.5.10
리소스가 과거에 존재했지만 영구 삭제되었고 전달 주소도 없습니다.
RFC 9110 §15.5.11
서버가 요청에 Content-Length를 요구합니다.
RFC 9110 §15.5.12
요청 헤더의 조건(If-Match, If-Unmodified-Since)이 충족되지 않았습니다.
RFC 9110 §15.5.13
요청 본문이 서버가 허용하는 크기를 초과했습니다.
RFC 9110 §15.5.14(이전 이름 Payload Too Large)
URL이 서버가 처리할 수 있는 길이를 넘었습니다.
RFC 9110 §15.5.15
요청 본문의 형식(Content-Type)을 서버가 지원하지 않습니다.
RFC 9110 §15.5.16
Range 헤더로 요청한 바이트 범위가 리소스 크기를 벗어났습니다.
RFC 9110 §15.5.17
Expect 헤더의 요구를 서버가 충족할 수 없습니다.
RFC 9110 §15.5.18
1998년 만우절 RFC의 농담으로 실제 HTTP에서 사용하면 안 됩니다.
RFC 2324(만우절 RFC). RFC 9110은 미사용으로 표시
요청이 해당 authority(도메인)의 응답을 제공할 수 없는 서버에 도착했습니다.
RFC 9110 §15.5.20
구문과 형식은 맞지만 의미상 처리할 수 없습니다(비즈니스 검증 실패).
RFC 9110 §15.5.21(이전 이름 Unprocessable Entity)
리소스가 잠겨 있습니다.
RFC 4918(WebDAV)
의존하는 작업이 먼저 실패해서 이 작업도 실패했습니다.
RFC 4918(WebDAV)
재생될 수 있는 요청을 처리하고 싶지 않다는 서버의 의사 표시입니다(TLS 1.3 0-RTT 조기 데이터).
RFC 8470
서버가 현재 프로토콜에서의 처리를 거부하고 Upgrade 헤더에 지정된 프로토콜로 전환할 것을 요구합니다.
RFC 9110 §15.5.22
업데이트 손실을 막기 위해 서버가 조건 헤더(If-Match 등)를 요구합니다.
RFC 6585
일정 시간 동안 요청을 너무 많이 보냈습니다(속도 제한).
RFC 6585
요청 헤더(단일 필드 또는 합계)가 너무 큽니다.
RFC 6585
저작권이나 정부 명령 등 법적 이유로 리소스를 제공할 수 없습니다. 번호는 소설 화씨 451에 대한 오마주입니다.
RFC 7725
서버가 자신의 실패 또는 정상 요청을 처리할 수 없음을 인지하고 있습니다. 나중에 다시 시도하면 성공할 수 있습니다.
서버가 예기치 않은 상황을 만나 요청을 완료하지 못했습니다.
RFC 9110 §15.6.1
요청을 완료하는 데 필요한 기능을 서버가 지원하지 않습니다(보통 알 수 없는 메서드).
RFC 9110 §15.6.2
게이트웨이/프록시로 동작하는 서버가 업스트림에서 잘못된 응답을 받았습니다.
RFC 9110 §15.6.3
서버가 일시적으로 요청을 처리할 수 없습니다(과부하 또는 점검). 보통 잠시뿐입니다.
RFC 9110 §15.6.4
게이트웨이/프록시가 업스트림 응답을 기다리다 시간 초과했습니다.
RFC 9110 §15.6.5
요청에 사용된 HTTP 주 버전을 서버가 지원하지 않습니다.
RFC 9110 §15.6.6
콘텐츠 협상 설정 오류로 루프가 생겼습니다.
RFC 2295
요청에 필요한 내용을 서버가 저장할 수 없습니다.
RFC 4918(WebDAV)
요청 처리 중 무한 루프를 감지했습니다.
RFC 5842(WebDAV)
서버가 처리하려면 요청을 더 확장해야 합니다.
RFC 2774(역사적)
네트워크 계층(캡티브 포털)에서 먼저 인증해야 합니다.
RFC 6585
어떤 RFC에도 없고 특정 서버의 로그나 CDN 오류 페이지에만 나타납니다. 알아 두면 문제 해결 시간을 크게 줄일 수 있습니다.
nginx가 아무 응답 없이 연결을 닫습니다. nginx 로그에만 나타납니다.
nginx 전용
서버가 응답하기 전에 클라이언트가 연결을 끊었습니다. nginx 로그에만 나타납니다.
nginx 전용
원본이 Cloudflare에 반환한 응답이 비어 있거나 잘못되었거나 헤더가 너무 컸습니다.
Cloudflare 전용
Cloudflare가 원본에 도달할 수 없습니다(TCP 연결 거부).
Cloudflare 전용
Cloudflare에서 원본으로의 TCP 연결이 시간 초과되었습니다.
Cloudflare 전용
Cloudflare가 원본으로 라우팅할 수 없습니다(DNS 또는 네트워크 경로 문제).
Cloudflare 전용
TCP는 연결되었지만 원본이 100초 안에 HTTP 응답을 반환하지 않았습니다.
Cloudflare 전용
Cloudflare와 원본 사이의 TLS 핸드셰이크가 실패했습니다.
Cloudflare 전용
Full (strict) 모드에서 원본 인증서가 만료·자체 서명·도메인 불일치입니다.
Cloudflare 전용