互联网草案 HTTP 的 RateLimit 头字段 2026 年 5 月
Polli 等 2026 年 11 月 24 日到期 [页]
工作组:
HTTPAPI
互联网草案:
draft-ietf-httpapi-ratelimit-headers-11
发布:
预期状态:
标准轨道
到期:
作者:
R. Polli
Team Digitale,意大利政府
A. Martinez
Red Hat
D. Miller
Microsoft

HTTP 的 RateLimit 头字段

摘要

本文档定义了 RateLimit-Policy 和 RateLimit HTTP 头字段,供服务器公布其配额策略和当前服务限制,从而使客户端能够避免被限流。

关于本文档

本说明将在作为 RFC 发布前移除。

本文档的状态信息可在 https://datatracker.ietf.org/doc/draft-ietf-httpapi-ratelimit-headers/ 找到。

本文档的讨论在 HTTPAPI 工作组邮件列表(mailto:httpapi@ietf.org)上进行, 其归档位于 https://mailarchive.ietf.org/arch/browse/httpapi/。 可在 https://www.ietf.org/mailman/listinfo/httpapi/ 订阅。 工作组信息可在 https://datatracker.ietf.org/wg/httpapi/about/ 找到。

本草案的源码和问题跟踪器可在 https://github.com/ietf-wg-httpapi/ratelimit-headers 找到。

本备忘录的状态

本互联网草案完全符合 BCP 78 和 BCP 79 的规定提交。

互联网草案是互联网工程任务组(IETF)的工作文档。 请注意,其他组织也可能以互联网草案的形式发布 工作文档。当前互联网草案列表位于 https://datatracker.ietf.org/drafts/current/

互联网草案是有效期最长为六个月的草案文档,并且可能在任何 时间被其他文档更新、替代或废止。将互联网草案用作参考 材料,或将其作为“进行中的工作”以外的形式引用,都是不合适的。

本互联网草案将于 2026 年 11 月 24 日到期。

目录

1. 引言

HTTP 客户端限流已经成为一种广泛实践,尤其是在 HTTP API 中。通常,执行限流的服务器会限制给定时间窗口内可接受请求的数量(例如 每秒 10 个请求)。有关 HTTP 中限流当前用法的更多信息,请参见附录 A

目前,服务器还没有一种标准方式来传达配额,以便客户端 能够对其请求进行节流从而防止错误。本文档定义了一组标准 HTTP 头 字段以支持限流:

这些字段支持建立复杂的限流策略,包括使用 多个可变时间窗口和动态配额,以及实现并发限制。

1.1. 目标

本文档的目标是:

互操作性:

标准化限流头的名称和语义,以便于 执行和采用。

韧性:

通过向客户端提供有助于节流其请求并防止 4xx 或 5xx 响应的信息, 提高 HTTP 基础设施的韧性。

文档:

通过消除在 API 文档中包含 详细配额限制及相关字段的需要,简化 API 文档。

以下功能不属于本文档的范围:

授权:

RateLimit 头字段并非用于支持授权或 其他类型的访问控制。

响应状态码:

RateLimit 头字段可以在成功响应(见 [HTTP] 的第 15.3 节)和 非成功响应中返回。本规范不涵盖非成功 响应是否计入配额使用,也不强制 RateLimit 值与返回状态码之间存在任何关联。

节流算法:

本规范不强制指定某种节流 算法。字段中发布的值,包括窗口大小,可以 静态或动态地评估。

服务级别协议:

传达的配额提示不意味着任何服务保证。服务器可以 在某些情况下对遵守服务器推荐限制的客户端进行限流。

1.2. 记号约定

本文档中的关键词 "MUST"、"MUST NOT"、"REQUIRED"、"SHALL"、"SHALL NOT"、"SHOULD"、"SHOULD NOT"、"RECOMMENDED"、"NOT RECOMMENDED"、 "MAY" 和 "OPTIONAL" 应按 BCP 14 [RFC2119] [RFC8174] 中的描述解释,但仅当它们 以如本文所示的全大写形式出现时才如此。

术语 Origin 应按 [WEB-ORIGIN] 的第 7 节中的描述解释。

本文档使用 [SF] 的第 3 节中的术语 List、Item 和 Integer 来指定语法和解析,并使用 “bare item”的概念。

本文档中的术语 “problem type” 应按 [PROBLEM] 中的描述解释。

2. 术语

配额:

配额是资源服务器用于限制 客户端请求的容量分配。该容量以配额单位衡量,客户端可以在 时间窗口内消耗这些配额单位。

配额单位:

配额单位是用于衡量 客户端活动的计量单位。

配额分区:

配额分区是服务器容量在不同 客户端、用户和拥有资源之间的划分。

时间窗口:

时间窗口表示与已分配 配额相关联的一段时间。

配额策略:

配额策略由服务器实现,用于调节指定配额分区内的 活动,该活动以配额单位量化,并限定在定义的时间窗口内。此 活动被限制在预定义的上限内,该上限称为配额。配额策略可以由 服务器公布,但并非必须公布,并且多个配额策略可以 影响客户端向服务器发出的给定请求。

服务限制:

服务限制是特定配额 策略下当前可用的配额,以及在已定义时,客户端可在其中使用不超过 可用配额的有效时间窗口。

List:

由 Items 组成的 [SF] list

Item:

带有一组 关联参数的 [SF] item

3. RateLimit-Policy 字段

"RateLimit-Policy" 响应头字段是由配额策略项(第 3.1 节)组成的非空 List[SF]。Item[SF] 值 MUST 是 String[SF]

该字段值 SHOULD 在一系列 HTTP 响应中保持一致。正是这一 特性将它与 RateLimit第 4 节字段区分开来,后者包含的信息 MAY 在 每个请求上变化。"RateLimit-Policy" 字段使客户端能够基于服务器提供的策略信息 控制自己的请求流。在限流约束高度 动态的场景中,更适合使用传达客户端可以响应的最新服务信息的 RateLimit 字段第 4 节。 适当时,服务器可以同时传达这两个字段。

配额策略项(第 3.1 节)列表可以按照 [SF] 的第 3.1 节所述拆分到同一 HTTP 响应中的多个 "RateLimit-Policy" 字段中。

   RateLimit-Policy: "burst";q=100;w=60,"daily";q=1000;w=86400

3.1. 配额策略项

配额策略 Item 包含策略标识符以及一组 Parameters[SF],这些参数包含有关 服务器为该策略分配容量的信息。

定义了以下参数:

q:

REQUIRED 的 "q" 参数表示此 策略分配的配额,以配额单位衡量。

qu:

OPTIONAL 的 "qu" 参数值传达与 "q" 参数相关联的配额单位。默认配额单位是 "requests"。

w:

OPTIONAL 的 "w" 参数值传达一个时间窗口。

pk:

OPTIONAL 的 "pk" 参数值传达与相应请求 相关联的分区键。

允许使用其他参数,并且可以将其视为注释。

实现或服务特定的参数 SHOULD 带有供应商标识符作为前缀, 例如 acme-policyacme-burst

此字段 MUST NOT 出现在 trailer 节中。

3.1.1. 配额参数

"q" 参数值 MUST 是非负 Integer。该值 表示在给定配额分区中为客户端活动分配的配额(以配额单位衡量)。

3.1.2. 配额单位 参数

"qu" 参数值传达适用于 配额(第 3.1.1 节)的配额单位。 该值 MUST 是 String。允许的值列在 RateLimit 配额 单位注册表第 10.3 节中。本规范定义 三种配额单位:

requests:

此值表示配额基于资源服务器处理的 请求数量。特定请求是否实际 消耗配额单位是实现特定的。

content-bytes:

此值表示配额基于资源服务器处理的 内容字节数量。

concurrent-requests:

此值表示配额基于资源服务器处理的 并发请求数量。

3.1.3. 窗口参数

"w" 参数值传达适用于 配额(第 3.1.1 节)的时间窗口。 时间窗口 MUST 是非负、非零的 Integer 值,以秒为单位表示一个间隔, 类似于 [HTTP] 的第 10.2.3 节中定义的 "delay-seconds" 规则。 不支持亚秒精度。

3.1.4. 分区键 参数

"pk" 参数值传达与 请求相关联的分区键。该值 MUST 是 Byte Sequence。服务器 MAY 使用分区键在 不同客户端和资源之间划分服务器容量。配额按 分区键分配。

3.2. RateLimit Policy 字段 示例

此字段 MAY 传达与配额相关联的时间窗口,如 下例所示:

   RateLimit-Policy: "default";q=100;w=10

这些示例展示了返回多个策略的情况:

   RateLimit-Policy: "permin";q=50;w=60,"perhr";q=1000;w=3600

以下示例展示了带有分区键的策略:

   RateLimit-Policy: "peruser";q=100;w=60;pk=:cHsdsRa894==:

以下示例展示了带有分区键和配额单位的策略:

   RateLimit-Policy: "peruser";q=65535;qu="content-bytes";w=10;pk=:sdfjLJUOUH==:

4. RateLimit 字段

服务器使用 "RateLimit" 响应头字段来传达特定分区键的配额策略的当前服务 限制。

该字段表示为由 服务限制项(第 4.1 节)组成的 List[SF]

服务限制项列表可以按照 [SF] 的第 3.1 节所述拆分到同一 HTTP 响应中的多个 "RateLimit" 字段中。

   RateLimit: "default";r=50;t=30

4.1. 服务限制项

每个服务限制 Item[SF] 标识与请求关联的配额策略(第 3.1 节),并包含带有当前服务限制信息的 Parameters[SF]

本规范定义了以下参数:

r:

此 REQUIRED 参数值传达已标识策略下 的可用配额(第 4.1.1 节)。

t:

此 OPTIONAL 参数值传达已标识策略下的有效窗口: 客户端可使用不超过可用配额的时间(第 4.1.2 节)。

pk:

OPTIONAL 的 "pk" 参数值传达与相应请求 相关联的分区键。

此字段 MUST NOT 出现在 trailer 节中。允许使用其他参数, 并且可以将其视为注释。

实现或服务特定的参数 SHOULD 带有供应商标识符作为前缀, 例如 acme-policyacme-burst

4.1.1. 可用配额 参数

"r" 参数表示已标识 策略下的可用配额。

它是以配额单位表示的非负 Integer。 客户端 MUST NOT 假定正的可用配额保证进一步 请求会被处理。 当可用配额较低时,它表示服务器可能很快对 客户端进行限流(见第 6 节)。

4.1.2. 有效 窗口参数

"t" 参数表示已标识 策略下的有效窗口:客户端可使用不超过可用 配额的秒数。

它是与 delay-seconds 规则兼容的非负 Integer, 因为:

  • 它不依赖时钟同步,并且能够抵御时钟调整 以及客户端和服务器之间的时钟偏差(见 [HTTP] 的第 5.6.7 节);

  • 当过多客户端在同一时间戳获得服务时, 它能缓解与惊群效应相关的风险。

客户端 MUST NOT 假定其所有服务限制会在有效窗口参数指示的时间后完全 恢复。服务器 MAY 在后续请求之间任意改变 可用配额和有效窗口;例如,在资源饱和的情况下或为实现滑动窗口策略。

4.1.3. 分区键 参数

"pk" 参数值传达与 请求相关联的分区键。该值 MUST 是 Byte Sequence。服务器 MAY 使用分区键在 不同客户端和资源之间划分服务器容量。配额按 分区键分配。

4.2. RateLimit 字段 示例

此示例展示了一个 RateLimit 字段,其可用配额为 50 个单位, 有效窗口为 30 秒:

   RateLimit: "default";r=50;t=30

此示例展示了一个没有时间窗口的分区键拥有 999 个请求的可用配额:

   RateLimit: "default";r=999;pk=:dHJpYWwxMjEzMjM=:

此示例展示了某应用在接下来 60 秒内拥有 300MB 可用配额:

   RateLimit: "default";r=300000000;t=60;pk=:QXBwLTk5OQ==:

5. 问题类型

5.1. 配额已超出

本节定义 "https://iana.org/assignments/http-problem-types#quota-exceeded" 问题类型。如果服务器希望 向客户端传达客户端发送的请求 超出了一个或多个配额策略,则 MAY 使用此问题类型。此问题类型定义扩展成员 "violated-policies" 为字符串数组,其值是配额 被超出的策略名称。

HTTP/1.1 429 Bad Request
Content-Type: application/problem+json

{
  "type": "https://iana.org/assignments/http-problem-types#quota-exceeded",
  "title": "Request cannot be satisfied as assigned quota has been exceeded",
  "violated-policies": ["daily","bandwidth"]
}

5.2. 临时降低 容量

本节定义 "https://iana.org/assignments/http-problem-types#temporary-reduced-capacity" 问题类型。如果 服务器希望传达由于服务器容量临时降低,客户端的请求当前 无法得到满足,则 MAY 使用此问题类型。服务器 MAY 选择 包含 RateLimit-Policy 字段来表示新的临时较低配额。此问题类型 定义扩展成员 "violated-policies" 为字符串数组,其值是 配额被超出的策略名称。

HTTP/1.1 503 Server Unavailable
Content-Type: application/problem+json

{
  "type": "https://iana.org/assignments/http-problem-types#temporary-reduced-capacity",
  "title": "Request cannot be satisfied due to temporary server capacity constraints",
  "violated-policies": ["hourly"]
}

5.3. 检测到异常使用

本节定义 "https://iana.org/assignments/http-problem-types#abnormal-usage-detected" 问题类型。服务器 MAY 使用此问题类型向客户端传达它检测到一种 请求模式,该模式暗示客户端存在无意或恶意行为。此 问题类型定义扩展成员 "violated-policies" 为字符串数组,其 值是配额被超出的策略名称。

HTTP/1.1 429 Too Many Requests
Content-Type: application/problem+json

{
  "type": "https://iana.org/assignments/http-problem-types#abnormal-usage-detected",
  "title": "Request not satisifed due to detection of abnormal request pattern",
  "violated-policies": ["hourly"]
}

6. 服务器行为

服务器 MAY 独立于响应状态码返回 RateLimit 头字段。 这包括被限流的响应。本文档不强制 RateLimit 头字段值与返回状态码之间存在任何关联。

服务器在重定向响应 (即带有 3xx 状态码的响应)中返回 RateLimit 头字段时应当谨慎,因为低可用配额可能会阻止客户端 发出请求。例如,给定下面的 RateLimit 头字段,客户端可能会决定 在跟随 "Location" 头字段之前等待 10 秒(见 [HTTP] 的第 10.2.2 节), 因为可用配额为 0。

HTTP/1.1 301 Moved Permanently
Location: /foo/123
RateLimit: "problemPolicy";r=0;t=10

如果响应同时包含 Retry-After 和 RateLimit 头字段, Retry-After 字段值 SHOULD NOT 引用早于有效 窗口结束的时间点。

使用 RateLimit 头字段的服务 MUST NOT 传达会暴露不希望公开的 请求量的值,并且 SHOULD 实现机制来限制可用配额与 有效窗口值之间的比例(见第 8.5 节);当配额策略使用较长时间窗口时,这一点尤为重要。

在某些条件下,服务器 MAY 在 后续请求之间人为降低 RateLimit 头字段值,例如响应拒绝服务攻击或在资源 饱和的情况下。

6.1. 生成分区 键

服务器 MAY 选择返回用于区分不同消费者或不同资源所分配配额的分区键。 分区服务器容量有广泛的策略, 包括按用户、按应用、按 HTTP 方法、按 资源,或这些值的某种组合。服务器 SHOULD 记录分区键 是如何生成的,以便客户端能够预测未来请求的键值,并判断 是否有足够配额可用于执行该请求。服务器应避免返回 包含敏感信息的分区键。服务器 SHOULD 仅使用请求中 存在的信息来生成分区键。

6.2. 性能 注意事项

服务器无需在每个响应中返回 RateLimit 头字段, 客户端需要考虑这一点。例如,关注性能的实现者 可能只在给定配额接近耗尽时提供 RateLimit 头字段。

关注响应字段大小的实现者可能会考虑 它们相对于内容长度的比例,或使用诸如 [HPACK] 之类的头压缩 HTTP 特性。

7. 客户端行为

客户端可以使用 RateLimit 头字段来判断关联的 请求是否遵守了服务器的配额策略,并作为后续请求是否会 成功的指示。然而,服务器在处理未来请求时可能会应用其他标准,因此 配额策略可能不能完全反映请求是否会成功。

例如,一个成功响应带有以下字段:

   RateLimit: "default";r=1;t=7

并不保证下一个请求会成功。服务器行为可能 受其他条件约束。

客户端负责确保返回的 RateLimit 头字段值 会导致在吞吐量和延迟方面合理的客户端行为 (见第 8.5 节第 8.5.1 节)。

接收 RateLimit 头字段的客户端 MUST NOT 假定未来响应会 包含相同的 RateLimit 头字段,或包含任何 RateLimit 头字段。

格式错误的 RateLimit 头字段 MUST 被忽略。

客户端 SHOULD NOT 在有效窗口参数表示的时间内超过可用 配额。

有效窗口参数的值是在响应时间生成的:知道存在显著网络延迟的客户端 MAY 相应地行动,并使用其他信息(例如 "Date" 响应头字段,或以其他方式收集的指标)来调度请求。

RateLimit-Policy 头字段中提供的细节是资料性的,MAY 被 忽略。

如果响应同时包含 RateLimit 和 Retry-After 字段,则 Retry-After 字段 MUST 优先,并且有效窗口 MAY 被忽略。

本规范不强制指定具体的节流行为,实现者可以 采用其偏好的策略,包括:

7.1. 使用分区 键

如果单个客户端很可能会发出消耗不同配额分配的请求,则 分区键对客户端很有用。例如,客户端代表不同用户发出请求,或请求具有独立配额分配的不同资源。

如果服务器记录了分区键生成算法,客户端 MAY 为未来请求生成分区键。使用此键,并与服务器返回的键进行比较, 客户端可以判断是否有足够的配额可用于执行 请求。

对于服务器的分区键生成算法未知的情况, 客户端 MAY 使用启发式方法,根据未来请求与先前请求的 相似性来猜测其是否会成功。

7.2. 中介

本节记录了 [HTTP] 的第 16.3.2 节中建议的注意事项。

不属于源服务基础设施且不了解 Origin Server 使用的配额策略语义的中介 SHOULD NOT 以传达更宽松配额策略的方式更改 RateLimit 头字段的值; 这包括移除 RateLimit 头字段。

在以下情况下,中介 MAY 以传达 更严格配额策略的方式更改 RateLimit 头字段:

  • 它了解 Origin Server 使用的配额单位语义;

  • 它实现了本规范,并执行比字段中传达的策略 更严格的配额策略。

即使中介推定请求可能不会得到服务,也 SHOULD 转发该请求; 返回 RateLimit 头字段的服务是执行所传达配额策略的唯一责任方, 并且它始终可以自由地服务传入请求。

本规范不强制中介在重试方面采取任何行为, 也不要求中介在遵守配额策略方面承担任何角色。例如, 代理在不通知客户端的情况下重传请求并因此消耗配额单位是合法的。

隐私注意事项第 9 节提供了关于 中介的进一步指导。

7.3. 缓存

[HTTP-CACHING] 定义了响应如何被存储并重用于后续请求, 包括带有 RateLimit 头字段的响应。 由于缓存响应中的 RateLimit 头字段信息可能不是最新的,因此 对于来自缓存的响应 SHOULD 忽略它们 (即具有正 current_age 的响应;见 [HTTP-CACHING] 的第 4.2.3 节)。

8. 安全注意事项

8.1. 限流并不能 阻止客户端发出请求

本规范不能阻止客户端发出请求。 服务器应始终实现机制以防止资源耗尽。

8.2. 信息披露

服务器不应向不受信任方披露可用于使其基础设施资源饱和的运行容量 信息。

虽然本规范不强制非成功响应是否 消耗配额, 但如果错误响应(如 401 (Unauthorized) 和 403 (Forbidden))计入配额, 恶意客户端可能会探测端点以获取另一用户的流量信息。

由于中介可能在用户代理事先不知情的情况下重传请求并消耗 配额单位, RateLimit 头字段可能向用户代理暴露中介的存在。

当分区键包含客户端应用或用户的识别信息时, 服务器应意识到冒充的潜在风险,并应用 适当的安全机制。

8.3. 可用配额单位 并非已获准的请求

RateLimit 头字段传达来自服务器的提示 给客户端,以帮助它们避免被限流排除。

客户端 MUST NOT 将可用配额参数第 4.1.1 节 视为服务级别协议。

在资源饱和的情况下,服务器 MAY 人为降低返回的 值,或不论所公布配额如何都不服务该请求。

8.4. 有效窗口的 可变性

有效窗口参数第 4.1.2 节 并不暗示服务器何时会增加可用配额。有效 窗口不一定在某个固定时间点结束。

后续请求可能返回更高的有效窗口值, 以限制并发或实现动态或自适应节流策略。

8.5. 资源耗尽

返回有效窗口值时,服务器必须意识到 许多被限流的客户端可能会在指定的同一时刻回来。

这同样适用于 Retry-After。

例如,如果配额每天在 18:00:00 重置, 并且你的服务器相应地返回有效窗口

   Date: Tue, 15 Nov 1994 18:00:00 GMT
   RateLimit: "daily";r=1;t=86400

则很可能所有客户端都会在 18:00:00 出现。

可以通过向有效窗口添加一些抖动来缓解这一点。

资源耗尽问题可能与使用 较长时间窗口的配额策略相关,因为用户代理可能偶然或故意 在显著更短的间隔内消耗其大部分配额单位。

这种行为甚至可能由所提供的 RateLimit 头字段触发。 以下示例描述了一个服务, 其未消耗的配额策略为每 1000 秒 10000 个配额单位。

RateLimit-Policy: "somepolicy";q=10000;w=1000
RateLimit: "somepolicy";r=10000;t=10

实现可用配额与有效 窗口之间简单比值的客户端可能推断出平均吞吐量为每秒 1000 个配额单位,而策略的 配额和窗口参数传达的平均值为每秒 10 个配额单位。 如果服务无法处理此类负载,则应返回更低的可用配额或 更高的有效窗口。 此外,用较短时间窗口的策略补充较长时间窗口的配额策略可缓解 这些风险。

8.5.1. 拒绝服务

RateLimit 头字段可能偶然或故意包含意外值。 例如,过高的可用配额可能被:

  • 恶意中介用于触发拒绝 服务攻击, 或消耗客户端资源以增强其请求;

  • 配置错误的服务器传递;

或者,较大的有效窗口可能抑制客户端联系 服务器(例如类似于收到 "Retry-after: 1000000")。

为了缓解此风险,客户端可以根据配额单位、时间窗口、并发请求或吞吐量 设置其认为合理的阈值,并 定义当 RateLimit 超过这些阈值时的一致行为。 例如,这意味着限制每秒最大请求数,或在有效窗口超过十分钟时实现 重试。

上述注意事项不限于 RateLimit 头字段, 而是适用于所有影响客户端后续请求行为的字段(例如 Retry-After)。

9. 隐私注意事项

根据限流请求采取行动的客户端 可能是可重新识别的(见 [PRIVACY] 的第 5.2.1 节), 因为它们会对可能只提供给它们的信息作出反应。 请注意,这也可能适用于其他字段(例如 Retry-After)。

由于限流通常在 客户端已被识别或画像的上下文中实现 (例如为不同用户分配不同配额单位), 这通常很少成为问题。

使用 RateLimit 头字段的隐私增强基础设施 可以定义具体技术来缓解重新识别风险。

10. IANA 注意事项

请求 IANA 更新两个注册表并创建一个新注册表。

10.1. 更新 HTTP 字段名 注册表

请向 "Hypertext Transfer Protocol (HTTP) Field Name Registry" 注册表([HTTP])添加以下条目:

表 1
字段名 结构化类型 状态 规范
RateLimit List 永久 RFC nnnn 的第 4 节
RateLimit-Policy List 永久 RFC nnnn 的第 3 节

10.2. 更新 HTTP 问题 类型注册表

请求 IANA 在位于 https://www.iana.org/assignments/http-problem-types 的 "HTTP Problem Types" 注册表中注册以下条目。

10.2.1. 注册 "quota-exceeded" 问题类型

类型 URI: https://iana.org/assignments/http-problem-types#quota-exceeded

标题:Quota Exceeded

推荐 HTTP 状态码:429

引用:本文档第 5.1 节

10.2.2. 注册 "temporary-reduced-capacity" 问题类型

类型 URI: https://iana.org/assignments/http-problem-types#temporary-reduced-capacity

标题:Temporary Reduced Capacity

推荐 HTTP 状态码:503

引用:本文档第 5.2 节

10.2.3. 注册 "abnormal-usage-detected" 问题类型

类型 URI: https://iana.org/assignments/http-problem-types#abnormal-usage-detected

标题:Abnormal Usage Detected

推荐 HTTP 状态码:429

引用:本文档第 5.3 节

10.3. RateLimit 配额单位 注册表

本规范建立 "Hypertext Transfer Protocol (HTTP) RateLimit Quota Units" 注册表,其位置为 https://www.iana.org/assignments/http-ratelimit-quota-units。注册依据指定专家的建议 完成,该专家由 IESG 或其代表任命。所有条目都需要规范 (Specification Required)([IANA],第 4.6 节)。

该注册表具有以下初始内容:

表 2
配额单位 引用 说明
request RFC nnnn
content-bytes RFC nnnn
concurrent-requests RFC nnnn

10.3.1. 注册 模板

RateLimit 配额单位注册表的注册模板 如下:

  • 配额单位:配额单位的名称。

  • 引用:指定 配额单位的文档引用。

  • 说明:关于配额单位的任何补充说明。

11. 参考文献

11.1. 规范性参考文献

[HTTP]
Fielding, R.,编Nottingham, M., 编J. Reschke,编"HTTP 语义"STD 97RFC 9110DOI 10.17487/RFC9110<https://www.rfc-editor.org/rfc/rfc9110>
[IANA]
Cotton, M.Leiba, B.T. Narten"RFC 中 IANA 注意事项章节的 编写指南"BCP 26RFC 8126DOI 10.17487/RFC8126<https://www.rfc-editor.org/rfc/rfc8126>
[PROBLEM]
Nottingham, M.Wilde, E.S. Dalal"HTTP API 的问题详情"RFC 9457DOI 10.17487/RFC9457<https://www.rfc-editor.org/rfc/rfc9457>
[RFC2119]
Bradner, S."用于 RFC 中表示要求级别的关键词"BCP 14RFC 2119DOI 10.17487/RFC2119<https://www.rfc-editor.org/rfc/rfc2119>
[RFC8174]
Leiba, B."RFC 2119 关键词中大写与 小写的歧义"BCP 14RFC 8174DOI 10.17487/RFC8174<https://www.rfc-editor.org/rfc/rfc8174>
[SF]
Nottingham, M.P. Kamp"HTTP 的结构化字段值"RFC 9651DOI 10.17487/RFC9651<https://www.rfc-editor.org/rfc/rfc9651>
[WEB-ORIGIN]
Barth, A."Web Origin 概念"RFC 6454DOI 10.17487/RFC6454<https://www.rfc-editor.org/rfc/rfc6454>

11.2. 资料性参考文献

[HPACK]
Peon, R.H. Ruellan"HPACK:HTTP/2 的头压缩"RFC 7541DOI 10.17487/RFC7541<https://www.rfc-editor.org/rfc/rfc7541>
[HTTP-CACHING]
Fielding, R.,编Nottingham, M., 编J. Reschke,编"HTTP 缓存"STD 98RFC 9111DOI 10.17487/RFC9111<https://www.rfc-editor.org/rfc/rfc9111>
[PRIVACY]
Cooper, A.Tschofenig, H.Aboba, B.Peterson, J.Morris, J.Hansen, M.R. Smith"互联网协议的隐私注意事项"RFC 6973DOI 10.17487/RFC6973<https://www.rfc-editor.org/rfc/rfc6973>
[RFC3339]
Klyne, G.C. Newman"互联网上的日期和时间:时间戳"RFC 3339DOI 10.17487/RFC3339<https://www.rfc-editor.org/rfc/rfc3339>
[RFC6585]
Nottingham, M.R. Fielding"附加 HTTP 状态码"RFC 6585DOI 10.17487/RFC6585<https://www.rfc-editor.org/rfc/rfc6585>
[UNIX]
The Open Group"The Single UNIX Specification, Version 2 - 6 Vol Set for UNIX 98"

附录 A. 限流与配额

服务器使用配额机制来避免系统过载、确保计算资源的公平 分配或执行其他策略——例如货币化。

基本的配额机制会限制给定时间 窗口内可接受请求的数量,例如每秒 10 个请求。

当配额被超出时,服务器通常不服务该请求,而是 以 4xx HTTP 状态码(例如 429 或 403)回复,或采用更激进的策略,如丢弃连接。

配额可以基于不同依据执行(例如按用户、按 IP、按地理区域等), 也可以在不同层级执行。例如,用户可能被允许发出:

此外,系统指标、统计信息和启发式方法可用于实现更 复杂的策略, 其中可接受请求数量和时间窗口会被动态计算。

为了帮助客户端对其请求进行节流, 服务器可以通过 HTTP 头字段公开用于评估配额策略的计数器。

这些响应头可以由 HTTP 中介添加,例如 API 网关和 反向代理。

在 Web 上,我们可以找到许多不同的限流头, 它们通常包含给定时间窗口内允许的请求数,以及窗口何时 重置。

常见选择是返回三个头,包含:

A.1. 互操作性 问题

限流中的一个主要互操作性问题是缺乏标准头, 因为:

  • 每种实现都会为 相同头字段名关联不同语义;

  • 头字段名不断增多。

因此,与不同服务器交互的用户代理可能需要处理 不同头, 或者位于不同反向代理之后的同一个应用接口 可能回复不同的限流头。

附录 B. 示例

B.1. 未 定义策略的响应

有些服务器可能不会在 RateLimit-Policy 头 字段中公开策略限制。客户端仍可使用 RateLimit 头字段对其请求进行节流。

B.1.1. 响应中的限流 信息

客户端耗尽了接下来 50 秒的配额。 限制和时间窗口在带外传达。

请求:

GET /items/123 HTTP/1.1
Host: api.example

响应:

HTTP/1.1 200 Ok
Content-Type: application/json
RateLimit: "default";r=0;t=50

{"hello": "world"}

由于字段值不一定与 响应状态码相关联, 后续请求不一定会失败。 下面的示例展示了服务器决定服务该请求, 即使可用配额为 0。 另一个服务器,或同一个服务器在其他负载条件下,可能会决定 转而限流该请求。

请求:

GET /items/456 HTTP/1.1
Host: api.example

响应:

HTTP/1.1 200 Ok
Content-Type: application/json
RateLimit: "default";r=0;t=48

{"still": "successful"}

B.1.2. 响应中的多个 策略

服务器使用两种不同策略来限制客户端的 请求:

  • 5000 个每日配额单位;

  • 1000 个每小时配额单位。

客户端在最初 14 小时内消耗了 4900 个配额单位。

尽管下一个小时限制为 1000 个配额单位, 最接近达到的限制是每日限制。

随后服务器暴露 RateLimit 头字段,以 告知客户端:

  • 每日配额只剩 100 个配额单位,并且 窗口将在 10 小时后结束;

服务器 MAY 选择省略返回小时策略,因为它 使用与每日策略相同的配额单位,且每日策略是 最接近耗尽的策略。

请求:

GET /items/123 HTTP/1.1
Host: api.example

响应:

HTTP/1.1 200 Ok
Content-Type: application/json
RateLimit: "dayLimit";r=100;t=36000

{"hello": "world"}

B.1.3. 用于 限制并发

RateLimit 头字段可用于限制并发, 在饱和情况下公布低于通常值的限制, 从而提高可用性。

服务器采用了每分钟 100 个配额单位的基本策略, 并在资源耗尽时调整返回值, 降低可用配额和有效窗口值。

2 秒后,客户端消耗了 40 个配额单位

请求:

GET /items/123 HTTP/1.1
Host: api.example

响应:

HTTP/1.1 200 Ok
Content-Type: application/json
RateLimit-Policy: "basic";q=100;w=60
RateLimit: "basic";r=60;t=58

{"elapsed": 2, "issued": 40}

在后续请求中——由于资源耗尽—— 服务器只公布 r=20

请求:

GET /items/123 HTTP/1.1
Host: api.example

响应:

HTTP/1.1 200 Ok
Content-Type: application/json
RateLimit-Policy: "basic";q=100;w=60
RateLimit: "basic";r=20;t=56

{"elapsed": 4, "issued": 41}

B.1.4. 在 被限流响应中使用

客户端耗尽了其配额,服务器通过发送 Retry-After 对其限流。

在此示例中,Retry-After 和 RateLimit 头 字段的值引用同一时刻, 但这不是要求。

429 (Too Many Request) HTTP 状态码仅作为 示例使用。

请求:

GET /items/123 HTTP/1.1
Host: api.example

响应:

HTTP/1.1 429 Too Many Requests
Content-Type: application/problem+json
Date: Mon, 05 Aug 2019 09:27:00 GMT
Retry-After: Mon, 05 Aug 2019 09:27:05 GMT
RateLimit: "default";r=0;t=5

{
"type": "https://iana.org/assignments/http-problem-types#quota-exceeded"
"title": "Too Many Requests",
"status": 429,
"violated-policies": ["default"]
}

B.2. 具有已定义 策略的响应

B.2.1. 通过参数指定 限流窗口

客户端在接下来 50 秒内还剩 99 个配额单位。 时间窗口通过 w 参数传达,因此我们知道 吞吐量为每分钟 100 个配额单位。

请求:

GET /items/123 HTTP/1.1
Host: api.example

响应:

HTTP/1.1 200 Ok
Content-Type: application/json
RateLimit: "fixedwindow";r=99;t=50
RateLimit-Policy: "fixedwindow";q=100;w=60
{"hello": "world"}

B.2.2. 带参数化窗口的动态限制

RateLimit-Policy 头字段传达的策略表明 服务器接受每分钟 100 个配额单位。

为避免资源耗尽,服务器人为降低 限流头中返回的实际限制。

随后 RateLimit 头字段公布 接下来 50 秒只有 9 个配额单位,以减慢客户端。

注意,服务器也可以降低 RateLimit 头字段中的其他 值:本规范 不强制后续响应中包含的字段值之间 存在任何关系。

请求:

GET /items/123 HTTP/1.1
Host: api.example

响应:

HTTP/1.1 200 Ok
Content-Type: application/json
RateLimit-Policy: "dynamic";q=100;w=60
RateLimit: "dynamic";r=9;t=50


{
  "status": 200,
  "detail": "Just slow down without waiting."
}

B.2.3. 用于回退和减速的动态限制

延续上一个示例,假设客户端等待 10 秒并 执行一个新请求,由于资源耗尽,服务器拒绝该请求 并进行回退,公布接下来 20 秒 r=0

服务器公布一个具有较低限制的更小窗口,以在 20 秒结束后让客户端在原始窗口剩余部分减速。

请求:

GET /items/123 HTTP/1.1
Host: api.example

响应:

HTTP/1.1 429 Too Many Requests
Content-Type: application/json
RateLimit-Policy: "dynamic";q=15;w=20
RateLimit: "dynamic";r=0;t=20

{
  "status": 429,
  "detail": "Wait 20 seconds, then slow down!"
}

B.3. 用于通过 Retry-After 回退并减速的动态限制

另一种方式是,在上一个示例开始的相同上下文中,我们 可以通过 Retry-After 向客户端传达相同信息,其 优势是服务器现在可以指定重试时间之后将适用的策略名义限制和 窗口,例如假设到那时资源耗尽 很可能已经消失,因此公布的策略不需要 调整,同时我们仍然成功地暂时停止请求,并降低当前窗口剩余部分的 速度。

请求:

GET /items/123 HTTP/1.1
Host: api.example

响应:

HTTP/1.1 429 Too Many Requests
Content-Type: application/json
Retry-After: 20
RateLimit-Policy: "dynamic";q=100;w=60
RateLimit: "dynamic";r=15;t=40

{
  "status": 429,
  "detail": "Wait 20 seconds, then slow down!"
}

注意,在最后这个响应中,客户端预期会遵守 Retry-After,并在指定的时间量内不发出请求; 而前一个示例不会强制客户端在有效窗口结束前停止 请求,因为即使请求很可能被拒绝,它仍然可以自由地 再次查询服务器。

B.3.1. 与多个 窗口一起使用

这是一种标准化方式,用于描述附录 B.1.2中详述的策略:

  • 5000 个每日配额单位;

  • 1000 个每小时配额单位。

客户端在最初 14 小时内消耗了 4900 个配额单位。

尽管下一个小时限制为 1000 个配额单位,最接近 达到的限制 是每日限制。

随后服务器暴露 RateLimit 头字段以告知 客户端:

  • 它只剩 100 个配额单位;

  • 有效窗口为 10 小时;

  • expiring-limit 为 5000。

请求:

GET /items/123 HTTP/1.1
Host: api.example

响应:

HTTP/1.1 200 OK
Content-Type: application/json
RateLimit-Policy: "hour";q=1000;w=3600, "day";q=5000;w=86400
RateLimit: "day";r=100;t=36000

{"hello": "world"}

常见问题

本节将在作为 RFC 发布前移除。

  1. 为什么要为节流定义标准字段?

    为了简化节流策略的执行,并使客户端能够约束其请求 以避免被限流。

  2. 我可以在被限流响应中使用 RateLimit 头字段吗(例如状态 码 429)?

    可以。

  3. 这些规范是否与 RFC 6585 绑定?

    不是。[RFC6585] 定义了 429 状态码,我们仅将其作为被限流请求的示例, 该请求也可以改用 403 或任何其他状态码。

  4. 为什么分区键是必要的?

    如果没有分区键,服务器实际上只能有一个作用域(也称为分区),这对 大多数服务来说并不实际,或者它需要在带外传达作用域。 这会阻碍可用于防止请求 被限流的通用连接器代码的开发。 许多 API 依赖 API key、用户身份或客户端身份来分配配额。 一旦单个客户端处理多个分区的请求,客户端就需要 知道相应的分区键,以便正确地根据已分配配额跟踪请求。

  5. 为什么使用 delay-seconds 而不是 UNIX Timestamp? 为什么不使用亚秒精度?

    使用 delay-seconds 与 Retry-After 保持一致,Retry-After 会在类似上下文中返回, 例如在 429 响应上。

    时间戳需要时钟同步协议 (见 [HTTP] 的第 5.6.7 节)。 这可能会有问题(例如时钟调整、时钟偏差、硬编码时钟 同步服务器故障、 IoT 设备等)。 此外,由于时钟调整,时间戳可能不是单调递增的。 参见 另一个 NTP 客户端故障故事

    我们没有使用亚秒精度,因为:

    • 它更容易受到系统时钟校正的影响, 例如通过 adjtimex() Linux 系统调用实现的校正;

    • 响应时间延迟可能使其不值得。关于该主题的简短讨论 见 httpwg 邮件列表

    • 几乎所有限流头实现都没有使用它。

  6. 我不应该限制并发而不是请求速率吗?

    你可以使用本规范在 HTTP 层限制并发 (见 {#use-for-limiting-concurrency}), 并帮助客户端塑造其请求,从而避免被限流排除。

    一种有问题的限制并发方式是丢弃连接, 特别是在连接被复用时(例如 HTTP/2), 因为这会导致客户端请求未被处理, 而这是我们希望避免的事情。

    限制并发的语义化方式是在资源饱和时返回 503 + Retry-After (例如抖动、连接队列过长、 未满足服务级目标等)。 饱和条件可以是动态的,也可以是静态的:所有这些都超出 当前文档的范围。

  7. remaining 参数的正值是否意味着对我 未来请求被服务的任何服务保证?

    不。FAQ 已整合到第 4.1.1 节中。

  8. quota-policy 定义是否过于复杂?

    你始终可以返回最简单的形式

RateLimit:"default";r=50;t=60

策略键清晰地将某策略的当前使用状态连接到已定义的 限制。 因此,对于以下字段:

RateLimit-Policy: "sliding";q=100;w=60;burst=1000
RateLimit-Policy: "fixed";q=5000;w=3600;burst=0
RateLimit: "sliding";r=50;t=44

值 "sliding" 标识所报告的策略。

  1. 中介可以更改 RateLimit 头字段吗?

    通常不应这样做,因为这可能导致请求未被处理。 不过,中介篡改 RateLimit 头字段也有合理用例, 例如当它们执行更严格的 quota-policies, 或当它们是服务的活动组件时。 在这些情况下,我们会将它们视为源基础设施的一部分。

  2. 为什么 w 参数只是资料性的? 它是否可以被客户端用于确定请求速率?

    在客户端和服务器紧密耦合的环境中, 非资料性的 w 参数可能没问题。以这种细节在大规模上传达策略会非常复杂, 且实现很可能不可互操作。因此我们决定将 w 保留为 资料性参数,并仅依赖 limit、remaining 和 reset 参数 来定义节流 行为。

  3. 我可以在 trailers 中使用 RateLimit 字段吗? 服务器通常会在创建响应之前确定请求是否在配额内,因此 RateLimit 字段值在那一刻应当已经可用。 支持 trailers 的唯一优势是,在响应较慢的情况下,它允许向客户端提供更及时的信息。 然而,这会使客户端实现在合并 headers 中的字段以及考虑丢弃 trailers 的中介方面更加复杂。 由于目前没有使用 trailers 的实现,我们决定将其作为 未来工作保留。

当前在 Web 上使用的 RateLimit 头字段

本节将在作为 RFC 发布前移除。

常用的头字段名包括:

也存在一些变体,其中窗口在 头字段名中指定,例如:

下面是一些互操作性问题:

RateLimit 的语义取决于窗口算法。 例如,滑动窗口策略可能导致 remaining 参数值与 当前吞吐量和最大吞吐量之间的比例相关。 例如:

RateLimit-Policy: "sliding";q=12;w=1
; using 50% of throughput, that is 6 units/s
RateLimit: "sliding";q=12;r=6;t=1

如果是这种情况,最优解是达到

RateLimit-Policy: "sliding";q=12;w=1
; using 100% of throughput, that is 12 units/s
RateLimit: "sliding";q=12;r=1;t=1

此时你应停止提高请求速率。

致谢

感谢 Willi Schoenborn、Alejandro Martinez Ruiz、Alessandro Ranellucci、 Amos Jeffries、 Martin Thomson、 Erik Wilde 和 Mark Nottingham 成为 这些规范的初始贡献者。 同时向最早的社区实现者致敬: Aapo Talvensaari、 Nathan Friedly 和 Sanyam Dogra。

除上述人员之外,本文档还很大程度上受益于 HTTPAPI 工作组中的广泛讨论, 包括 Rich Salz 和 Julian Reschke。

变更

本节将在作为 RFC 发布前移除。

自 draft-ietf-httpapi-ratelimit-headers-08 以来

本节将在作为 RFC 发布前移除。

  • 添加了问题类型

  • 澄清了何时使用 RateLimit-Policy 与 RateLimit 字段

自 draft-ietf-httpapi-ratelimit-headers-07 以来

本节将在作为 RFC 发布前移除。

  • 将两个字段重构为标识策略并 使用参数的 Items 列表

  • 添加了配额单位参数

  • 添加了分区键参数

自 draft-ietf-httpapi-ratelimit-headers-03 以来

本节将在作为 RFC 发布前移除。

  • 在 RateLimit-Policy 中拆分策略信息 #81

自 draft-ietf-httpapi-ratelimit-headers-02 以来

本节将在作为 RFC 发布前移除。

  • 处理节流作用域 #83

自 draft-ietf-httpapi-ratelimit-headers-01 以来

本节将在作为 RFC 发布前移除。

  • 更新 IANA 注意事项 #60

  • 使用结构化字段 #58

  • 重组文档 #67

自 draft-ietf-httpapi-ratelimit-headers-00 以来

本节将在作为 RFC 发布前移除。

  • 使用 I-D.httpbis-semantics,其中包括引用 delay-seconds 而不是 delta-seconds。#5

作者地址

Roberto Polli
Team Digitale,意大利政府
意大利
Alejandro Martinez Ruiz
Red Hat
Darrel Miller
Microsoft