限流响应头

限流响应头(X-RateLimit-* / RateLimit-* / Retry-After)告诉客户端当前的限流状态、剩余配额和重试时机。

#type / concept #status / evergreen #tech / dev / backend #resource / http #protocol / http

[!info] related notes

限流响应头

一句话定义

限流响应头告诉客户端:你的请求配额是多少、还剩多少、什么时候重试。前端或 SDK 根据这些头做退避、提示和重试。

它要解决什么问题

API 限流是保护后端服务的基本手段。但光返回 429 状态码不够——客户端需要知道:

  • 我还能发多少请求?(限流上限)
  • 我还剩多少配额?(剩余次数)
  • 我什么时候可以重试?(重试时机)

两套风格

X-RateLimit-*(非标准但广泛使用)

X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 42
X-RateLimit-Reset: 1716540000
含义
X-RateLimit-Limit时间窗口内的总配额
X-RateLimit-Remaining当前窗口内剩余配额
X-RateLimit-Reset配额重置的 Unix 时间戳

RateLimit-*(较新的 IETF 草案)

RateLimit-Limit: 1000
RateLimit-Remaining: 42
RateLimit-Reset: 60
含义
RateLimit-Limit时间窗口内的总配额
RateLimit-Remaining当前窗口内剩余配额
RateLimit-Reset配额重置前的秒数(不是时间戳)

注意 RateLimit-ResetX-RateLimit-Reset 的区别:前者是相对秒数,后者是绝对时间戳。

Retry-After

配合 429 或 503 状态码使用,告诉客户端多久后重试。

HTTP/1.1 429 Too Many Requests
Retry-After: 60

或用 HTTP 日期格式:

HTTP/1.1 503 Service Unavailable
Retry-After: Sun, 24 May 2026 10:30:00 GMT

常见搭配

# 限流
HTTP/1.1 429 Too Many Requests
Retry-After: 60
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1716540000

# 服务不可用
HTTP/1.1 503 Service Unavailable
Retry-After: 120

前端如何使用

退避重试

async function fetchWithRetry(url, options, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    const res = await fetch(url, options);

    if (res.status === 429) {
      const retryAfter = res.headers.get("Retry-After");
      const waitMs = retryAfter
        ? parseInt(retryAfter) * 1000
        : Math.pow(2, i) * 1000;  // 指数退避
      await new Promise(r => setTimeout(r, waitMs));
      continue;
    }

    return res;
  }
  throw new Error("Max retries exceeded");
}

显示剩余配额

const res = await fetch("/api/data");
const remaining = res.headers.get("X-RateLimit-Remaining");

if (remaining !== null && parseInt(remaining) < 10) {
  showToast("API 调用次数即将用完,请稍后再试");
}

跨域场景

限流头是自定义响应头,跨域时前端 JS 默认读不到。需要后端配合:

Access-Control-Expose-Headers: X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, Retry-After

否则:

res.headers.get("X-RateLimit-Remaining") // null

边界与易混淆点

  • X-RateLimit- 不是 RFC 标准*:是约定俗成的实践。不同 API 提供商的实现可能有差异。
  • RateLimit- 是 IETF 草案*:draft-ietf-httpapi-ratelimit-headers,尚未正式成为 RFC,但已被部分 API 采用。
  • Retry-After 的值是秒数或 HTTP 日期:不是毫秒,不是 Unix 时间戳(除非是 X-RateLimit-Reset)。
  • 429 不一定带限流头:有些 API 只返回 429 状态码,不提供详细限流信息。这时前端只能用指数退避。
  • 限流窗口设计影响用户体验:滑动窗口比固定窗口更平滑,但实现更复杂。
创建于 2026/5/24 更新于 2026/5/27