요약

  • HTTP 429는 서버가 정의한 한도를 넘어선 요청 흐름을 기술한다. 이 코드는 요청이 잘못 구성되었다고 말하지 않고, 자연인을 식별하지도 않으며, 요청을 어떻게 세어야 하는지 정의하지도 않는다.
  • RFC 6585는 설명을 권장하고Retry-After를 허용했지만, 지연은 예약이 아니라 안내이다. 이후의 시도는 여전히 당시의 정책, 용량, 인가 및 자원 상태에 직면한다.
  • 이 코드는 숨겨진 계산을 복구에 필요한 만큼은 보이게 만든다. 공정성은 여전히 운영자의 신원 키, 범위, 계수기 위치와 거절 비용에 달려 있으며, 공격 상황에서는 표준이 대신 연결을 끊는 것을 허용한다.

다섯 번째 열 번째 요청은 깨져 있지 않았다

가입된 계정마다 시간당 50개의 요청을 받아들이는 API를 상상해 보라. 처음 50개의 메시지는 형식에 맞을 수 있다. 그 다음 하나는 몇 순간 전에 성공한 요청과 바이트 단위까지 동일할 수 있다. 그 자격 증명은 여전히 유효할 수 있고, 대상은 여전히 존재하며, 작업 자체는 여전히 의미가 있을 수 있다. 달라진 것은 메시지가 아니다. 메시지 뒤의 계수기가 선을 넘었다.

HTTP가 이 조건에 대한 특정한 답을 갖기 전에는, 서비스가 일반적인 클라이언트 오류를 반환하거나 서버 전체가 실패한 것처럼 과부하를 내보일 수 있었다. 어느 설명도 클라이언트가 내려야 할 결정을 드러내지 않았다. 요청을 고쳐야 하는가, 자격 증명을 바꿔야 하는가, 다른 자원을 선택해야 하는가, 기다려야 하는가, 멈춰야 하는가? 2012년 4월, RFC 6585는 더 정밀한 응답으로429 Too Many Requests를 등록했다.

그 정밀함은 신중하게 경계 지어져 있다. 이 RFC는 사용자가 주어진 시간 안에 너무 많은 요청을 보냈다고 말한다. 응답이 그 조건을 설명해야 하며Retry-After를 포함할 수 있다고 말한다. 그리고 더 함축적인 것도 말한다. 이 명세는 원점이 사용자를 어떻게 식별하는지, 요청을 어떻게 세는지 정의하지 않는다고.

이 코드는 판정을 표준화했지, 그 뒤에 있는 장부를 표준화하지는 않았다.

“사용자”는 계수 체계 안의 슬롯이다

일상 언어에서 사용자는 사람이다. 속도 제한기에서 세어지는 대상은 인증된 계정, API 키, 세션 쿠키, 테넌트, 출발지 주소 또는 운영자가 선택한 그 조합일 수 있다. RFC 6585는 자격 증명과 상태를 지니는 쿠키를 예로 든다. 그 어느 쪽도 한 사람을 가리킨다고 보증하지는 않는다.

이 구분은 네트워크의 가장자리에서 중요하다. 수천 명의 가입자가 하나의 통신사급 NAT 주소를 통해 나타날 수 있다. 한 사무실이 모든 직원 트래픽을 하나의 이그레스로 보낼 수 있다. 프라이버시 릴레이는 의도적으로 한 사람의 신원을 서비스에 보이는 주소와 분리한다. 가장 저렴한 초기 제한기가 주소만 센다면, 서로 무관한 사람들이 서로의 트래픽 이력을 상속받을 수 있다. 그 결과 나오는 429는 로컬에서 계산된 계수기에 대한 유효한 프로토콜 응답이지, 거절된 각 개인이 개인 허용량을 초과했다는 증명이 아니다.

인증은 다른 경계를 제공하지만 완전하지는 않다. 하나의 계정이 여러 기기를 구동할 수 있다. 하나의 자격 증명은 공유되거나 도난당할 수 있다. 키는 프로세스보다 고객 계약을 더 신뢰할 수 있게 식별할 수 있다. 운영자는 할당하려는 용량에 맞는 신원 증거를 선택하고, 거절된 클라이언트가 복구할 수 있을 만큼 그 선택을 충분히 공개해야 한다.

이것은 인터넷 권위에 관한 더 넓은 원칙을 따른다. 신호는 그 증거가 지탱할 수 있는 만큼의 권력만을 지녀야 한다. IP 주소는 주소 범위의 계수기를 지탱할 수 있다. 그것 자체만으로 사람에 대한 판단을 승인할 수는 없다.

계수기의 범위는 결정의 일부다

RFC 6585는 자원별 계수, 서버 전체 계수, 서버 집합 사이의 계수를 허용한다. 이 가능성들은 같은 세 자리 숫자를 내보내면서 서로 다른 체계를 만들어 낸다.

자원별 계수기는 비용이 큰 검색 엔드포인트를 계정 설정을 차단하지 않고 보호할 수 있다. 서버 전체 계수기는 설명하기 쉽지만 값싼 헬스 체크가 비싼 보고서와 같은 단위를 소모하게 만들 수 있다. 테넌트 전체 계수기는 상업적 할당에 맞출 수 있는데, 모든 요청이 올바른 테넌트에 매핑된다는 전제가 필요하다. 플릿 전체 계수기는 여러 지역에 걸쳐 하나의 허용량을 강제할 수 있지만, 분산 증분이 어떻게 수렴하는지, 그리고 그 상태 서비스가 이용 불가능할 때 무슨 일이 일어나는지 결정해야 한다.

“요청 하나”조차 스스로 정의되지 않는다. 단위는 도착인가, 인증된 요청인가, 승인된 작업인가, 완료된 응답인가? 리디렉트는 또 하나의 단위를 소모하는가? 취소된 HTTP/2 스트림은 세어지는가? 애플리케이션이 요청하지 않은 프록시 재시도는 어떤가? 유선 상태 코드는 이 질문들에 답할 수 없다. 그것들은 원점 뒤의 운영 체계에 속하기 때문이다.

부정확한 계수기는 합법적 작업을 거부하거나 과잉 부하를 허용하면서도 내보내는 모든 429를 통법적으로 정확하게 유지할 수 있다. 따라서 프로토콜 적합성은 계수 정책의 감사가 아니다.

시계는 권한을 부여하지 않고 조언할 수 있다

RFC 6585는 429 응답이Retry-After를 포함하는 것을 허용한다. 현재 HTTP 의미론에서 이 필드는 HTTP 날짜 또는 음수가 아닌 지연 초 수를 담을 수 있다. 그 차이는 실용적이다. 절대 날짜는 당사자들이 공유 시간 기준을 해석해야 하고, 지연 초는 응답의 수신부터 시작한다. 어느 형태도 예약을 만들지 않는다.

클라이언트는 후속 요청 전에 기다리라는 요청을 받은 것이다. 미래의 대기열에서 첫 자리를 산 것은 아니다. 제안된 경계에서 다른 트래픽이 이미 용량을 소모했을 수 있고, 서비스가 정책을 바꿨을 수 있고, 자격 증명이 더 이상 인가되지 않았을 수 있고, 기반 자원이 바뀌었을 수 있다. 백로그 전체를 같은 초에 내보내는 클라이언트는 보이는 시간을 너무 문자 그대로 따른 바로 그 이유로 새로운 쇄도를 만들 수 있다.

그러므로 바람직한 복구는 서버의 힌트와 로컬 절제를 결합한다. 유한한 재시도, 기상 시각의 일정 분산, 더 이상 유용하지 않은 작업의 만료, 부작용을 인지하는 처리. 타이머가 끝났다고 해서 POST가 반복해도 안전해지는 것은 아니다.Retry-After는 시간을 기술할 뿐, 불확실한 전송 실패 상황에서 작업의 최종 결과를 확정하지는 않는다.

429는 더 작은 503이 아니다

429와503 Service Unavailable은 모두Retry-After와 함께 나타날 수 있지만, 거절을 서로 다른 표면에 놓는다. RFC 9110은 503을 과부하 또는 예정된 유지보수로 인한 일시적 처리 불능으로 정의한다. 서비스가 자신의 현재 무능을 설명하는 것이다. 429는 작동적 한도를 서버의 속도 정책이 선택한 주체가 너무 많은 요청을 보낸 데 돌린다.

이 구분 덕분에 한 계정이나 한 자원이 제한되는 동안 다른 작업이 계속될 수 있다. 동시에 정직의 의무도 부과한다. 서비스 전체 용량 장애를, 4xx 코드가 책임을 옮기기 더 편리하다는 이유만으로 한 클라이언트의 잘못된 행위처럼 서술해서는 안 된다. 반대로, 하나의 소진된 테넌트 예산이 모든 클라이언트에게 서비스 전체가 이용 불가능하다고 말할 필요도 없다.

현실에는 둘이 함께 존재할 수 있다. 플랫폼은 계정별 할당량과 전역 과부하 보호를 함께 가질 수 있다. 유용한 상태는 실제로 취해진 분기를 설명하는 것이다. 코드는 제어 표면을 드러내야 하지, 장식해서는 안 된다.

거절에는 비용이 있다

RFC 6585에서 가장 중요한 한도는 보안 고려 사항에 나타난다. 서버가 공격을 받는 중이라면 모든 요청에 429로 응답하는 것은 자원을 소모한다. 그래서 이 표준은 모든 경우에 그 응답을 요구하지 않는다. 연결을 끊거나 다른 조치를 취하는 편이 더 적절할 수 있다.

거절을 만들어 내려면 메시지를 해석하고, 암호학을 완수하고, 자격 증명을 찾고, 분산 계수기를 조회하고, 설명을 렌더링하고, 암호화된 바이트를 보내야 할 수 있다. 그 모든 것을 “아니오”라고 말하기 전에 수행하는 방어 체계는 보호하려던 자원을 잃을 수 있다. 가장자리는 더 일찍 거부할 수 있지만, 의도된 범위를 적용할 만큼 충분히 신뢰할 수 있는 정보가 있을 때만 가능하다. 더 약한 신원으로 더 일찍 행동하면 계산 비용은 줄지만 부수적 거절은 늘어난다.

이것은 상호운용성의 실패가 아니다. 비례하는 권위에 관한 진술이다. 설명은 체계가 감당할 수 있을 때 가치가 있다. 설명 자체가 공격 표면이 될 때는 생존이 침묵을 요구할 수 있다.

거절이 재사용 가능한 콘텐츠가 되어서는 안 되는 이유

RFC 6585는 캐시가 429 응답을 저장하는 것을 금지한다. 이 규칙은 판정의 성격에서 따라 나온다. 속도 제한 결과는 시간, 귀속된 주체, 요청 범위, 그리고 현재 계수기에 묶여 있다. 이를 나중에 재생하면 이미 끝난 창을 이어 붙이거나, 한 주체의 거절을 다른 요청 맥락에 적용할 수 있다.

중개자는 자신이 소유한 속도 정책을 집행하고 새로운 거절을 생성할 수 있다. 그러나 오래된 429를 재사용 가능한 표현 콘텐츠로 취급할 수는 없다. 이 금지는 조건부 계산 결정을 여전히 계수기를 관찰할 수 있는 체계에 붙잡아 둔다.

세 자리 숫자가 증명할 수 없는 것

429 응답은 사용자를 인증하지 않는다. 악의적 의도, 악용, 심지어 공정한 할당조차 증명하지 않는다. 계수기가 정확한지, 최종 일관성을 지니는지, 이용 불가능한지 드러내지 않는다. 기다리면 성공할 것이라고 약속하지 않는다. 모든 과부하 서버가 자신을 설명하도록 요구하지도 않는다.

할 수 있는 일은 더 좁지만 가치 있다. 서버가 속도 정책의 거절을 잘못 구성된 콘텐츠 및 일반적 서비스 불능과 구분하게 한다. 클라이언트가 즉각적인 반복이 도움이 되지 않을 것임을 인식하게 한다. 운영자가 실행 가능한 설명과 신뢰할 수 있는 지연을 제공하면, 자동 복구는 더 차분하고 예측 가능해질 수 있다.

따라서 429의 역사는 HTTP가 할당량을 발명한 이야기가 아니다. HTTP가 사적 용량 결정을 공통의 목소리로 말하게 하면서, 그 목소리를 보편적 권위로 바꾸는 것은 거부한 이야기이다.

출처