> For the complete documentation index, see [llms.txt](https://www.axisnow.io/zh/docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://www.axisnow.io/zh/docs/guide/rate-limiting-api.md).

# API 限速实战

使用 AxisNow 在边缘为 API 分层限速

有些异常流量从单个请求上看不出来：撞库脚本的每个 POST 都格式正确，反复调用搜索接口的每次请求都合法，持有真实密钥高频调用推理接口的请求也能通过认证。它们唯一的共同特征是同一来源的请求过多。认证判断请求是谁，访问控制判断请求是否允许，都不以频率为依据；速率控制补上这一维度。API 的调用方是程序，无法完成人机验证，所以超限后的处置是拒绝或封禁，而不是挑战。

本文给出一套推荐的 API 限速配置，共四条规则，用我们平台上一个 AI 代理站点的真实配置做示范。网站页面的防刷是另一套思路，另文介绍。

## 开始之前 <a href="#before-you-start" id="before-you-start"></a>

需要准备：

* 一个已经接入 AxisNow 边缘的站点
* API 接口的路径清单
* 调用方携带密钥的请求头名，本例是 `Authorization`

先确认两件事：

* **真实客户端 IP**：按 IP 计数的规则才需要关心。边缘前面有负载均衡或其它 CDN 等前置代理时，连接地址是代理的，要在[真实客户端 IP](/zh/docs/edge/policies/real-ip.md)插件里配置从哪个请求头还原真实地址，否则所有请求会被算在同一个 IP 上。
* **合理估算限速阈值**：以正常流量的业务基线为参考，先看日常水位和峰值再确定数值。计数按边缘节点各自进行，不做跨节点汇总，流量分布在多个节点时，阈值按单个节点承接的量设定，而不是按总量，见[工作原理 › 计数的作用域](/zh/docs/edge/policies/rate-limiting/how-it-works.md#scope)。

## 推荐的四条规则 <a href="#recommended-rules" id="recommended-rules"></a>

案例站点的 api 域名面向程序调用，提供推理接口（如 `/v1/chat/completions`）和登录、搜索等业务接口（如 `/api/user/login`），推理接口的请求带 `Authorization` 头。

![](https://3062891322-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FKDgGNU6JXG7cqYCk2aCz%2Fuploads%2Fgit-blob-1fb66a1418539137f316ee1fcfe4266c01cb9d51%2Frate-limiting-api-01.png?alt=media)

推荐的配置是四条规则，由严到松：

<table data-search="false"><thead><tr><th width="32">#</th><th width="130">规则</th><th width="170">条件</th><th width="150">计数 Key 与阈值</th><th width="90">动作</th><th>理由</th></tr></thead><tbody><tr><td>1</td><td>攻击拦截-匿名写入与认证限速</td><td>api 域名，登录、二次验证、注册、重置密码等 8 个路径，POST</td><td>客户端 IP 地址<br>3 秒内不超过 10 请求</td><td>IP 封禁 5 分钟</td><td>请求没有身份可用，只能按 IP 计数；正常用户 3 秒内不会提交 10 次登录，超限基本是撞库或批量注册</td></tr><tr><td>2</td><td>攻击拦截-大响应接口限速</td><td>api 域名，频道搜索接口</td><td>客户端 IP 地址 + 请求 URI 路径<br>3 秒内不超过 20 请求</td><td>IP 封禁 5 分钟</td><td>单次开销是普通接口的几十倍，按全站总量设限约束不了它；按 IP + 路径计数，刷这个接口不影响同一用户访问其它接口</td></tr><tr><td>3</td><td>API-模型与推理接口限速</td><td>api 域名，<code>/v1/</code> 下的推理与模型接口</td><td>请求头 authorization + 请求 URI 路径<br>3 秒内不超过 30 请求</td><td>阻断，返回 429</td><td>调用方是服务端程序，一个出口 IP 后面是同一客户的多个进程，按 IP 计数要么误伤要么失效；按密钥计数，配额落在每个调用方上</td></tr><tr><td>4</td><td>API-按 IP 兜底</td><td>api 域名，不限路径</td><td>客户端 IP 地址<br>10 秒内不超过 500 请求</td><td>阻断，返回 429</td><td>按密钥计数管不住没带密钥和每次换随机密钥的请求，它们会全部打到源站去做鉴权；这条按 IP 算总量兜住，阈值要高于单个 IP 的正常峰值</td></tr></tbody></table>

四条的主线是**按什么计数**：没有身份的接口按 IP，有身份的接口按密钥，开销大的接口单独计数，最后按 IP 兜底。顺序是严格的在前、宽松的在后：规则按序号依次评估，各自计数，前面的规则一旦执行阻断或封禁，这个请求就不再进入后面的规则。

动作只有两种，跟着计数 Key 选：

* **IP 封禁**：封禁时长内丢弃该 IP 到这个节点的全部连接，不区分域名和端口，开销最低。适合按 IP 计数、超限几乎一定是攻击的匿名接口。代价是共享出口后面的其他人会一起被封，而且波及您的所有站点。
* **阻断**：只拒绝触发超限的那个计数 Key，返回 429，同一 IP 的其它密钥、其它接口不受影响；429 也是 API 客户端能识别并退避的信号。按密钥计数的接口必须用它。

{% hint style="info" %}
**新规则先用观察动作。** 观察不干预请求，只记录超限的那些。观察一段时间、至少覆盖一个业务高峰，确认超限的确实是异常来源，再改成阻断或封禁；切换只是改动作，已有计数照常累积。查看数据的方法见下文[洞察与日志](#insight-and-logs)。
{% endhint %}

## 第一条：匿名写入接口按 IP <a href="#step-login-rule" id="step-login-rule"></a>

条件直观，阈值容易确定，误伤面小，用它做操作示范。新建规则，先把随机生成的规则名改为能表明意图的名称，然后从上到下填写四张卡片：**条件**决定哪些请求进入这条规则，**计数 Key** 决定按谁计数，**限速阈值**决定多少算超限，**限速动作**决定超限后如何处置。

{% stepper %}
{% step %}

### 条件 <a href="#login-rule-conditions" id="login-rule-conditions"></a>

选**自定义**，加三行，同一组内是 And 关系：

* **请求 Host** **在以下各项之中** api 域名（规则作用于租户下所有站点，不加这一行会影响其它域名）
* **请求 URI 路径** **在以下各项之中** `/api/user/login`、`/api/user/register`、`/api/user/password/reset` 等 8 个路径。列在这里的就是这条规则限速的对象，请求命中其中任意一个就进入计数
* **HTTP 请求方法** **在以下各项之中** `POST`，让跨域调用的 OPTIONS 预检不计入

路径按原样比较，区分大小写、不做等价归一，填的要和源站实际处理的写法一致。
{% endstep %}

{% step %}

### 计数 Key <a href="#login-rule-counting-key" id="login-rule-counting-key"></a>

打开**常用组合**，选**客户端 IP 地址**，预览显示 `ip.src`。登录前的请求没有身份可用，只能按 IP 计数；启用了真实客户端 IP 插件时，这里是还原出来的真实地址。
{% endstep %}

{% step %}

### 阈值 <a href="#login-rule-threshold" id="login-rule-threshold"></a>

**时间窗口**填 3，**请求数**填 10。卡片下方的参考图是全站流量的总和，而计数按每个 IP 分别累计，单个 IP 远小于总量，所以它只用来看日常水位和历史尖峰的数量级；阈值是否合适，靠观察模式验证。
{% endstep %}

{% step %}

### 动作与保存 <a href="#login-rule-action" id="login-rule-action"></a>

**限速动作**先选**观察**，确认无误后改成 **IP 封禁** 5 分钟。**优先级**选**第 1 个**，这是整组里最严格的规则。点击**确认**，规则创建并立即启用。
{% endstep %}
{% endstepper %}

{% hint style="info" %}
**封禁时长宁短勿长。** 封禁没有手动解除，5 分钟足够打断绝大多数攻击；持续重试的来源到期会再次被封，确认有这种情况再调长。
{% endhint %}

## 第二条：高消耗接口按 IP + 路径 <a href="#step-heavy-endpoint" id="step-heavy-endpoint"></a>

步骤和第一条相同，不同的是：

* **条件**：请求 Host 为 api 域名，请求 URI 路径只填频道搜索这一个接口。
* **计数 Key**：常用组合选**客户端 IP 地址 + 请求 URI 路径**，预览显示 `ip.src + http.request.uri.path`。这条规则只覆盖一个路径，IP + 路径和只按 IP 效果一样；选它是为了以后在条件里加入第二个高消耗接口时各接口分别计数，刷搜索不会影响导出。
* **阈值**：3 秒内不超过 20 请求，比登录接口宽一倍。搜索是正常用户会连续用的功能，输入一个词改一个词是常态，要给这种行为留出空间。
* **动作与优先级**：先观察，之后改为 IP 封禁 5 分钟；优先级第 2 个。

## 第三条：推理接口按 API 密钥 <a href="#step-api-key" id="step-api-key"></a>

`/v1/chat/completions` 这类接口是这个站点的核心业务，调用方不是浏览器前的用户，而是开发者的服务器。一个企业客户的几十个进程走同一个出口 IP，按 IP 限速要么阈值放到失去意义，要么一个进程异常导致整个客户被封。调用方唯一稳定的身份是请求头里的密钥。

* **条件**：请求 Host 为 api 域名，请求 URI 路径在 `/v1/chat/completions`、`/v1/responses`、`/v1/models` 等推理与模型接口之中。
* **计数 Key**：常用组合选 **API 配额限速**下的 **API 密钥 + 请求 URI 路径**，在弹出的对话框里填请求头名 `authorization`。
* **阈值**：3 秒内不超过 30 请求。这是 3 秒内的突发配额，不是匀速每秒 10 次，30 次可以瞬间用完，随后被限制 3 秒。
* **动作与优先级**：先观察，之后改为**阻断**，**返回页面**，状态码保持默认的 429 Too Many Requests；优先级第 3 个。页面内容可以上传一个 JSON 文件让客户端解析出错误原因，响应的 `Content-Type` 固定为 `text/html`，客户端应按状态码和响应头识别。被拒绝的响应带 `X-RateLimit` 系列响应头、不带 `Retry-After`，而 OpenAI 兼容的 SDK 默认按 `Retry-After` 退避，请在接口文档里说明改读 `X-RateLimit-Reset`，见[工作原理 › 响应头](/zh/docs/edge/policies/rate-limiting/how-it-works.md#response-headers)。

## 第四条：按 IP 的下限 <a href="#step-ip-floor" id="step-ip-floor"></a>

按密钥计数覆盖不了两种请求：未携带 `Authorization` 的，全部落入同一个空值计数器；每个请求更换随机密钥的，每次都是新计数器，永远不会超限。两种都会把流量送到源站做鉴权，所以第三条之后要加一条按 IP 的下限。

* **条件**：请求 Host 为 api 域名，不限路径。
* **计数 Key**：常用组合选**客户端 IP 地址**。兜底统计的是一个 IP 的总量，不分路径，流量分散到多个接口上同样计入。
* **阈值**：10 秒内不超过 500 请求，起步值。一个企业出口后面几十个进程的合法调用也在这个计数器里，阈值要高于单个 IP 的正常峰值，取观察到的最大值的数倍。
* **动作与优先级**：阻断，返回 429；优先级最后 1 个。

参数出处见[防护场景 › API 防 CC 兜底](/zh/docs/edge/policies/rate-limiting/use-cases.md#api-flood)。调用方不带密钥的 API，跳过第三条，以这一条为主。如果推理接口匿名也能调用，在第三条的条件里加一行「**请求头** `authorization` **存在**」，让它只对带密钥的请求生效，匿名流量全部交给这一条。

## 洞察与日志 <a href="#insight-and-logs" id="insight-and-logs"></a>

规则配置完成后，按顺序确认三个问题。

**规则是否生效**：看速率控制的规则列表。顶部指标卡是最近 24 小时的超限请求数和占比，按阻断、IP 封禁、观察、挑战分项，右边一张是此刻封禁中的 IP 数。规则表里每条规则有一列 24 小时命中趋势，注意这里是命中数不是超限数：命中是请求匹配了条件、进入了计数，超限才是触发了动作。第四条这种兜底规则命中率接近 100%、超限只有零点几，两个数都正常。

**拦截的来源**：点规则列表右上角的**洞察**。灰度期间主要看**观察**这张卡，它统计观察动作下超限但照常放行的请求，是判断阈值是否合适的依据：几乎为零，阈值太宽；集中在少数来源，阈值合适；分散在大量普通来源，阈值太紧或者计数 Key 选错了。改为阻断或封禁后看**阻断**和 **IP 封禁**两张卡，下面的命中时间线能看出攻击的节奏。**命中来源**按 Host 或国家列出命中最多的前 10 个，能看出攻击集中在哪个域名、哪个地区。

![](https://3062891322-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FKDgGNU6JXG7cqYCk2aCz%2Fuploads%2Fgit-blob-3daeac0a0d26fb0aa02f32e0b1810f948dbbc9bd%2Frate-limiting-api-02.png?alt=media)

**IP 封禁记录**列出每一次封禁：客户端 IP、触发的规则、触发的边缘节点、封禁状态。「24h 内第 N 次」标签直接显示这个 IP 当天被封了多少次，几十次说明攻击者在持续重试，封禁到期即再次发起，可以考虑延长封禁时长。

<figure><img src="https://3062891322-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FKDgGNU6JXG7cqYCk2aCz%2Fuploads%2FTN4tH4Dd8AohH5c7ML5g%2Fimage.png?alt=media&amp;token=7d9b985d-483a-4420-82ac-cc8c8958f062" alt=""><figcaption></figcaption></figure>

**具体请求**：动作格上的**查看日志**、规则列表里的命中数、命中来源的某一行，点进去都是全局洞察的日志检索，已经按动作、规则或来源预筛选，再叠加客户端 IP、URI 等条件即可。排查误伤时按规则筛选，看超限的 IP 和 URI 里有没有已知的客户或合作伙伴；一个请求命中多条规则时并列显示，能看出它最终是被哪条处置的。两点说明：日志经过采样，明细可能查不全，但上面的指标不受影响；处于 IP 封禁期内的来源不进入处理，没有日志，封禁本身记在 IP 封禁记录里。

每张卡、每张图的定义见[洞察与日志](/zh/docs/edge/policies/rate-limiting/insight.md)。

## 回退 <a href="#rollback" id="rollback"></a>

限速的回退都不需要删除规则：

* **误伤了正常用户**：把动作改回**观察**，流量立即恢复，计数继续累积，可以一边看数据一边调整阈值。改阈值、改条件不会清空已有计数，改计数维度等于换一套新计数器。
* **停用整条规则**：在规则列表关闭**生效状态**开关。
* **IP 已经被封**：封禁到期自动解除，目前没有手动解封；关闭规则能阻止再次封禁，已生效的封禁会持续到期满。
* **内部来源被限**：在通用访问控制里给监控探针、合作伙伴固定 IP 配一条**放行**规则并选择跳过速率控制，不要在每条限速规则里分别排除。公司出口、校园网后面的调用方也会被按 IP 的规则误伤，这是按 IP 计数的固有代价，所以本文把按 IP 的规则只用在匿名写入接口、高消耗接口和最后的下限上，阈值按“单个用户不可能达到”设定。

## 更多场景 <a href="#more-scenarios" id="more-scenarios"></a>

其它常见场景在插件文档的[防护场景](/zh/docs/edge/policies/rate-limiting/use-cases.md)里各有一组可直接套用的参数：

* [匿名写接口](/zh/docs/edge/policies/rate-limiting/use-cases.md#anonymous-write)：在本文第一条的基础上只统计登录失败（响应状态码 401、403），或按账号字段而不是按 IP 计数，阈值可以压得更低。
* [开放 API 调用方配额](/zh/docs/edge/policies/rate-limiting/use-cases.md#api-quota)：按密钥给总配额，配合没带密钥的兜底规则。
* [扫描器与随机 URL](/zh/docs/edge/policies/rate-limiting/use-cases.md#scanners)：只统计 404 响应，按 IP 封禁，正常用户几乎不受影响。
* [分层](/zh/docs/edge/policies/rate-limiting/use-cases.md#layering)：多条规则同时启用时的推荐顺序。

网站页面的防刷思路不同：访问者是真人，动作首选挑战而不是封禁。每个字段的含义见[配置项](/zh/docs/edge/policies/rate-limiting/configuration.md)，读数不符合预期时查[常见问题](/zh/docs/edge/policies/rate-limiting/faq.md)。

## 后续扩展方向 <a href="#future-extensions" id="future-extensions"></a>

速率控制目前按请求数计数，这是成本的粗略代理。对 AI 代理这样的业务，一次 `/v1/chat/completions` 调用 30 个 Token 和 4 万个 Token 的成本差三个数量级。我们接下来会补齐：

* **消耗型限速**：按 Token 数、响应字节数或源站返回的成本分数计数，而不只是请求次数。
* **递进封禁**：同一来源反复触发时，封禁时长自动加长。目前封禁时长是固定值，到期后攻击者再次发起，只会再被封同样长的时间。

我们非常乐意听取您对产品和体验的反馈，您可以加入[社区电报群组](https://t.me/AxisNowGroupZh)。


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://www.axisnow.io/zh/docs/guide/rate-limiting-api.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
