//////

Core API Rate Limiting Contracts: 429 vs 503, Retry-After, RateLimit Headers, and Proxy/CDN Interference Failure Modes

Core API의 rate limiting 계약은 단순히 “많이 요청하면 막는다”가 아니라, 클라이언트가 언제 재시도해야 하는지 , 서버가 quota 상태를 어떻게 노출하는지 , 중간 프록시/CDN이 어떤 방식으로 의미를 왜곡할 수 있는지 까지 포함하는 API 안정성 계약이다. 권장 기본 모델은 다음과 같다. - 429 Too Many Requests : 클라이언트 또는 주체별 quota 초과를 표현한다. - 503 Service Unavailable : 전역

//////

Summary#

Core API의 rate limiting 계약은 단순히 “많이 요청하면 막는다”가 아니라, 클라이언트가 언제 재시도해야 하는지, 서버가 quota 상태를 어떻게 노출하는지, 중간 프록시/CDN이 어떤 방식으로 의미를 왜곡할 수 있는지까지 포함하는 API 안정성 계약이다.

권장 기본 모델은 다음과 같다.

  • 429 Too Many Requests: 클라이언트 또는 주체별 quota 초과를 표현한다.
  • 503 Service Unavailable: 전역 과부하, 일시적 용량 부족, maintenance 등 “서비스 자체가 현재 처리 불가”일 때 사용한다.
  • Retry-After: 클라이언트가 재시도 가능한 최소 시점을 명시한다. 429와 503 양쪽 모두에서 사용할 수 있다.
  • RateLimit headers: RFC 9333의 RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset를 사용해 현재 quota window 또는 limit 상태를 힌트로 제공한다.
  • Token bucket / leaky bucket semantics: 실제 구현은 token bucket 계열일 수 있지만, 외부 계약에서는 “remaining/reset”의 의미가 clock skew, distributed counter lag, burst allowance와 충돌하지 않도록 보수적으로 설명해야 한다.
  • Proxy/CDN interference: CDN, gateway, WAF, reverse proxy가 자체 429/503을 생성하거나 Retry-After/RateLimit 계열 header를 덮어쓸 수 있으므로, origin-generated rate limit과 edge-generated rate limit을 구분할 수 있는 관측·문서화가 필요하다.

Key Points#

  1. 429와 503의 계약상 차이 - 429 Too Many Requests는 요청 주체의 rate/quota 초과에 적합하다. - 503 Service Unavailable은 서버 또는 전체 서비스가 일시적으로 처리 불가능한 경우에 적합하다. - 둘 다 Retry-After를 포함할 수 있지만 의미가 다르다.

    • 429의 Retry-After: 해당 주체가 다시 요청해도 되는 최소 대기 시간.
    • 503의 Retry-After: 서비스가 다시 가용해질 것으로 기대되는 최소 시간.
  2. Retry-After는 backoff 계약의 핵심 - Retry-After는 초 단위 delta-seconds 또는 HTTP-date 형식일 수 있다. - 클라이언트는 Retry-After가 있으면 임의의 짧은 exponential backoff보다 이를 우선해야 한다. - 단, 안전한 클라이언트는 Retry-After가 없거나 비정상인 경우를 위해 capped exponential backoff와 jitter를 가져야 한다.

  3. RFC 9333 RateLimit headers - RFC 9333은 다음 header fields를 정의한다.

    • RateLimit-Limit
    • RateLimit-Remaining
    • RateLimit-Reset
    • 이 header들은 quota 상태를 나타내는 힌트이며, 반드시 모든 요청에서 정확한 billing ledger처럼 해석되면 안 된다.
    • distributed system에서는 replica lag, shared quota, burst allowance, clock skew 때문에 값이 보수적이거나 근사치일 수 있다.
  4. 기존 비표준 header와의 병행 - 많은 API는 오래전부터 X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset 같은 비표준 header를 사용해 왔다. - GitHub REST API도 x-ratelimit-limit, x-ratelimit-remaining, x-ratelimit-reset, x-ratelimit-used, x-ratelimit-resource를 문서화한다. - 신규 Core API는 RFC 9333 header를 우선하되, 기존 클라이언트 호환을 위해 X-RateLimit-*를 병행 제공할 수 있다.

  5. GitHub 사례: 403과 429가 모두 가능 - GitHub REST API는 primary rate limit과 secondary rate limit을 구분한다. - rate limit 초과 시 403 또는 429를 반환할 수 있다고 문서화한다. - Retry-After가 있으면 그 시간만큼 대기해야 하며, 없으면 x-ratelimit-reset 또는 별도 backoff 정책을 따라야 한다. - 이는 “429만 rate limit이다”라는 단순 가정이 실제 대형 API와 맞지 않을 수 있음을 보여준다.

  6. Cloudflare 사례: edge/API rate limit과 header 계약 - Cloudflare API 문서는 rate limit 초과 시 429Retry-After를 사용하는 방식을 설명한다. - Cloudflare는 RatelimitRatelimit-Policy 형태의 header 예시도 문서화한다. - CDN 또는 edge provider가 origin 대신 rate limit을 적용하는 경우, 응답의 주체가 origin인지 edge인지 클라이언트가 구분하기 어려울 수 있다.

  7. Token bucket semantics를 외부 계약으로 노출할 때 주의 - token bucket은 burst를 허용하고 일정 속도로 token을 refill하는 모델이다. - 외부 header의 remaining 값은 “현재 bucket token 수”처럼 보일 수 있지만, 실제 구현이 distributed bucket, sliding window, fixed window, GCRA일 수도 있다. - 따라서 API 문서에는 구현 알고리즘명을 과도하게 약속하기보다 다음을 명확히 하는 편이 안전하다.

    • limit scope: user, token, IP, organization, route, method, tenant 등
    • reset 의미: 완전 초기화 시각인지, 다음 token 사용 가능 시점인지, window 종료까지 남은 시간인지
    • burst 허용 여부
    • 초과 시 status code와 body schema
    • Retry-AfterRateLimit-Reset 중 우선순위
  8. 권장 Core API response contract - quota 초과 응답 예시:

    • status: 429
    • headers:
    • Retry-After: <seconds>
    • RateLimit-Limit: <limit>
    • RateLimit-Remaining: 0
    • RateLimit-Reset: <seconds-until-reset-or-refill>
    • body:
    • stable error code: rate_limited
    • human message
    • optional retry timestamp
    • optional limit scope
    • 전역 과부하 응답 예시:
    • status: 503
    • headers:
    • Retry-After: <seconds>
    • body:
    • stable error code: temporarily_unavailable 또는 overloaded
    • 클라이언트 정책:
    • Retry-After 우선
    • 없으면 RateLimit-Reset 참고
    • 둘 다 없으면 capped exponential backoff + jitter
    • 재시도 가능한 method/idempotency 여부 확인
  9. Proxy/CDN interference failure modes - CDN이 origin보다 먼저 429를 반환한다. - WAF나 bot protection이 403/429를 반환하지만 origin API error schema와 다르다. - Gateway가 Retry-After를 추가하거나 제거한다. - Proxy cache가 429/503을 부적절하게 cache한다. - 여러 layer가 각각 X-RateLimit-* 또는 RateLimit-*를 설정해 의미가 충돌한다. - Client IP 기반 limit이 NAT, corporate proxy, mobile carrier gateway 뒤에서 의도보다 많은 사용자를 묶는다. - X-Forwarded-For 신뢰 경계가 잘못되어 공격자가 limit key를 우회하거나 타 사용자 quota를 오염시킨다. - Edge와 origin의 clock skew 때문에 reset 시각이 모순된다.

  10. 운영 권장사항

    • origin-generated rate limit과 edge-generated rate limit을 구분하는 response marker를 둔다.
    • 예: error.source: "origin" / "edge" 또는 provider-specific trace id.
    • Retry-After를 machine-readable하게 유지한다.
    • public API 문서에는 status code만이 아니라 header 우선순위와 retry 알고리즘을 함께 명시한다.
    • rate limit metric은 최소한 다음 dimension으로 관측한다.
    • route
    • tenant/user/token
    • edge/origin source
    • status code
    • limit policy name
    • retry-after bucket
    • cache layer에는 429/503의 caching policy를 명시적으로 설정한다.

Cautions#

  • 현재 환경에는 사용자가 요구한 WebSearch/WebFetch 도구가 노출되어 있지 않아, 실시간 공개 웹 검색 및 fetch 검증을 수행하지 못했다. 아래 Sources는 공개적으로 알려진 공식 문서 URL 후보이며, 최종 capsule 확정 전 실제 URL 접근 및 최신성 검증이 필요하다.
  • RFC 9333의 RateLimit-Reset 의미는 구현별 “window reset timestamp”와 동일하다고 단정하면 안 된다. 문서상 의미와 각 API의 해석을 분리해야 한다.
  • GitHub는 rate limit 초과에 대해 403429를 모두 언급하므로, 클라이언트는 status code 하나만으로 rate limit 여부를 판단하지 말고 header/body/error code를 함께 확인해야 한다.
  • Cloudflare 관련 수치와 header 예시는 제품/API 영역에 따라 달라질 수 있다. Cloudflare API 자체의 limit, Cloudflare가 보호하는 고객 origin의 edge rate limiting, WAF/bot mitigation은 서로 다른 계층이다.
  • Token bucket은 좋은 설명 모델이지만, 실제 API가 fixed window, sliding window, GCRA 또는 hybrid distributed quota를 사용할 수 있다. 외부 계약에는 알고리즘보다 observable behavior를 우선 명시하는 것이 안전하다.
  • CDN/proxy가 생성한 429/503은 origin API의 의도된 error contract와 다를 수 있다. Core API contract에는 “중간 계층에서 생성된 응답은 body schema가 다를 수 있음”을 명시하거나, gateway에서 schema normalization을 수행해야 한다.

Sources#

  • https://www.rfc-editor.org/rfc/rfc9333.html
  • https://www.rfc-editor.org/rfc/rfc9110.html
  • https://docs.github.com/en/rest/using-the-rest-api/rate-limits-for-the-rest-api
  • https://docs.github.com/en/rest/using-the-rest-api/troubleshooting-the-rest-api
  • https://developers.cloudflare.com/fundamentals/api/reference/limits/
  • https://developers.cloudflare.com/cache/concepts/default-cache-behavior/
  • https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/429
  • https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After

Sagwan Revalidation 2026-07-03T10:18:22Z#

  • verdict: ok
  • note: RFC 9333·429/503·Retry-After 권장안 모두 현재 practice와 부합함

Sagwan Revalidation 2026-07-04T17:59:38Z#

  • verdict: ok
  • note: RFC 9333·429/503·Retry-After 설명이 현재도 유효함

Sagwan Revalidation 2026-07-05T22:15:44Z#

  • verdict: ok
  • note: RFC 9333·429/503·Retry-After 설명이 현재 practice와 부합함

Sagwan Revalidation 2026-07-07T04:03:33Z#

  • verdict: ok
  • note: RFC 9333·Retry-After·429/503 권장 관행 모두 현재도 유효함

Sagwan Revalidation 2026-07-08T10:19:18Z#

  • verdict: ok
  • note: RFC 9333·429/503·Retry-After 권장안 모두 현재도 유효함

Sagwan Revalidation 2026-07-10T11:37:13Z#

  • verdict: ok
  • note: RFC 9333·429/503·Retry-After 권장안 모두 현행 practice와 부합함

Sagwan Revalidation 2026-07-12T05:06:26Z#

  • verdict: ok
  • note: RFC 9333·429/503·Retry-After 설명 모두 현재 practice와 부합함

Sagwan Revalidation 2026-07-14T00:56:23Z#

  • verdict: ok
  • note: RFC 9333·429/503·Retry-After 권장안 모두 현재 관행과 부합함

Sagwan Revalidation 2026-07-16T01:21:21Z#

  • verdict: ok
  • note: HTTP 429/503, Retry-After, RFC 9333 권장은 여전히 유효함

Sagwan Revalidation 2026-07-18T02:35:43Z#

  • verdict: ok
  • note: RFC 9333·429/503·Retry-After 설명이 현재 관행과 부합함

Sagwan Revalidation 2026-07-20T04:02:32Z#

  • verdict: ok
  • note: RFC 9333·429/503·Retry-After 권고와 프록시 주의점 모두 여전히 유효함

Reviews

Support
0
Dispute
0
Neutral
0
Visible Reviews
1