# 速率限制 (/docs/concepts/rate-limit)

了解如何在 Better Auth 中配置速率限制，包括 IP 地址检测、IPv6 支持、自定义速率限制时间窗口、存储后端、错误处理和按端点设置规则。



Better Auth 包含内置的速率限制器，以帮助管理流量和防止滥用。默认情况下，在生产模式下，速率限制器设置为：

* 窗口：10 秒
* 最大请求数：100 次请求

<Callout type="warning">
  使用 `auth.api` 进行的服务器端请求不受速率限制影响。速率限制仅适用于客户端发起的请求。
</Callout>

您可以通过将 `rateLimit` 对象传递给 `betterAuth` 函数轻松自定义这些设置。

```ts title="auth.ts"
import { betterAuth } from "better-auth";

export const auth = betterAuth({
    rateLimit: {
        window: 10, // 时间窗口，单位秒
        max: 100, // 窗口内最大请求数
    },
})
```

开发模式下默认禁用速率限制。若想启用，请将 `enabled` 设置为 `true`：

```ts title="auth.ts"
import { betterAuth } from "better-auth";

export const auth = betterAuth({
    rateLimit: {
        enabled: true,
        //...其他选项
    },
})
```

除了默认设置，Better Auth 还为特定路径提供自定义规则。例如：

* `/sign-in/email`：在 10 秒内限制为 3 次请求。

此外，插件也会为特定路径定义自定义规则。例如，`twoFactor` 插件具有自定义规则：

* `/two-factor/*`：在 10 秒内限制为 3 次请求。

这些自定义规则确保敏感操作受到更严格的限制保护。

## 配置速率限制 [#配置速率限制]

### 连接 IP 地址 [#连接-ip-地址]

速率限制通过连接的 IP 地址来跟踪用户发起的请求数量。默认检查的请求头为 `x-forwarded-for`，这在生产环境中常用。如果您使用其他请求头来跟踪用户的 IP 地址，则需要指定它。

```ts title="auth.ts"
import { betterAuth } from "better-auth";

export const auth = betterAuth({
    //...其他选项
    advanced: {
        ipAddress: {
          ipAddressHeaders: ["cf-connecting-ip"], // Cloudflare 特定请求头示例
      },
    },
    rateLimit: {
        enabled: true,
        window: 60, // 时间窗口，单位秒
        max: 100, // 窗口内最大请求数
    },
})
```

默认情况下，Better Auth 不信任以逗号分隔的转发 IP 链，因为在追加型代理后，最左侧的 `X-Forwarded-For` 标记由客户端控制。位于反向代理或负载均衡器之后时，请执行以下任一操作。

将 `ipAddressHeaders` 指向您的代理设置的单个可信请求头（例如 `x-real-ip` 或 `cf-connecting-ip`）：

```ts title="auth.ts"
export const auth = betterAuth({
    advanced: {
        ipAddress: {
            ipAddressHeaders: ["x-real-ip"],
        },
    },
})
```

或者在 `trustedProxies` 中列出您的代理。Better Auth 会从右到左遍历链路，跳过受信任的跳数，并将第一个不受信任的地址视为客户端：

```ts title="auth.ts"
export const auth = betterAuth({
    advanced: {
        ipAddress: {
            // 您的代理地址，不要使用会同时覆盖客户端的大范围私有网段
            trustedProxies: ["192.0.2.10", "10.0.0.0/24"],
        },
    },
})
```

<Callout type="warning">
  `trustedProxies` 仅会解析转发请求头链，无法验证直接发送方。请确保您的源站只能通过这些代理访问，并让每个代理覆盖或清理转发的 IP 请求头。否则，客户端可以直接设置 `X-Forwarded-For` 并伪造其地址。
</Callout>

#### IPv6 地址支持 [#ipv6-地址支持]

Better Auth 会自动规范 IPv6 地址，以防止攻击者通过使用同一 IPv6 地址的不同表示形式绕过限制（例如 `2001:db8::1` 与 `2001:0db8:0000:0000:0000:0000:0000:0001`）。这样可以确保对于速率限制来说，所有同一 IPv6 地址的表现形式都被视为相同。

此外，IPv4 映射的 IPv6 地址（例如 `::ffff:192.0.2.1`）会自动转换为它们的 IPv4 形式（`192.0.2.1`），防止攻击者通过在 IPv4 和 IPv6 表示之间切换绕过速率限制。

#### IPv6 子网速率限制 [#ipv6-子网速率限制]

默认情况下，IPv6 地址按 `/64` 子网进行速率限制，而不是按单个地址。互联网服务提供商和云服务提供商会分配 IPv6 前缀（通常住宅用户为 `/64`，参见 [RFC 6177](https://datatracker.ietf.org/doc/html/rfc6177)），而不是单个地址，因此如果按单个地址计数，某个客户端就可以在不耗尽限制的情况下轮换使用 `2^64` 个源地址。

如果您的部署需要不同的分配边界，可以通过 `ipv6Subnet` 选项覆盖前缀长度：

```ts title="auth.ts"
export const auth = betterAuth({
    //...其他选项
    advanced: {
        ipAddress: {
            ipv6Subnet: 56, // 按 /56 子网进行速率限制（住宅 ISP 分配）
        },
    },
    rateLimit: {
        enabled: true,
        window: 60,
        max: 100,
    },
})
```

常见的 IPv6 前缀长度：

* `128`：单个 IPv6 地址。限制最严格。仅在您控制网络且信任每个地址都对应不同客户端时才安全。
* `64`（默认）：/64 子网。典型的家庭或企业分配。
* `56`：/56 子网。参见 [RFC 6177](https://datatracker.ietf.org/doc/html/rfc6177) 的住宅 ISP 分配。
* `48`：/48 子网。更大的网络分配。
* `32`：/32 子网。ISP 级别分配。

接受 `0` 到 `128` 之间的任意整数前缀长度；上述值最为常见。

<Callout type="info">
  IPv6 子网配置仅影响 IPv6 地址。IPv4 地址始终按单个地址进行速率限制。
</Callout>

### 速率限制时间窗口 [#速率限制时间窗口]

```ts title="auth.ts"
import { betterAuth } from "better-auth";

export const auth = betterAuth({
    //...其他选项
    rateLimit: {
        window: 60, // 时间窗口，单位秒
        max: 100, // 窗口内最大请求数
    },
})
```

您也可以为特定路径传递自定义规则。

```ts title="auth.ts"
import { betterAuth } from "better-auth";

export const auth = betterAuth({
    //...其他选项
    rateLimit: {
        window: 60, // 时间窗口，单位秒
        max: 100, // 窗口内最大请求数
        customRules: {
            "/sign-in/email": {
                window: 10,
                max: 3,
            },
            "/two-factor/*": async (request)=> {
                // 自定义函数用于返回速率限制窗口和最大请求数
                return {
                    window: 10,
                    max: 3,
                }
            }
        },
    },
})
```

如果想要禁用特定路径的速率限制，可以设置为 `false`，或者自定义规则函数返回 `false`。

```ts title="auth.ts"
import { betterAuth } from "better-auth";

export const auth = betterAuth({
    //...其他选项
    rateLimit: {
        customRules: {
            "/get-session": false,
        },
    },
})
```

### 存储 [#存储]

默认情况下，速率限制数据存储在内存中，这可能不适合许多用例，尤其是在无服务器环境中。为此，您可以使用数据库、二级存储或自定义存储来存储速率限制数据。

**使用数据库**

```ts title="auth.ts"
import { betterAuth } from "better-auth";

export const auth = betterAuth({
    //...其他选项
    rateLimit: {
        storage: "database",
        modelName: "rateLimit", // 可选，默认使用 "rateLimit"
    },
})
```

请确保运行 `migrate` 命令在数据库中创建速率限制表：

<CodeBlockTabs defaultValue="npm" groupId="persist-install">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="npm">
      npm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="pnpm">
      pnpm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="yarn">
      yarn
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="bun">
      bun
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="npm">
    ```bash
    npx auth@latest migrate
    ```
  </CodeBlockTab>

  <CodeBlockTab value="pnpm">
    ```bash
    pnpm dlx auth@latest migrate
    ```
  </CodeBlockTab>

  <CodeBlockTab value="yarn">
    ```bash
    yarn dlx auth@latest migrate
    ```
  </CodeBlockTab>

  <CodeBlockTab value="bun">
    ```bash
    bun x auth@latest migrate
    ```
  </CodeBlockTab>
</CodeBlockTabs>

<Callout type="info">
  `migrate` 命令仅在使用内置的 Kysely 适配器时有效。如果您使用 Prisma、Drizzle 或其他 ORM，请先执行 `npx auth@latest generate`，然后通过您使用的 ORM 迁移工具应用 schema。详情请参阅 [Better Auth CLI](/docs/concepts/cli)。
</Callout>

**使用二级存储**

如果已经配置了[二级存储](/docs/concepts/database#二级存储)，您可以使用它来存储速率限制数据。

```ts title="auth.ts"
import { betterAuth } from "better-auth";

export const auth = betterAuth({
    //...其他选项
    rateLimit: {
		storage: "secondary-storage"
    },
})
```

**自定义存储**

如果上述方式都不适合您的用例，您可以实现一个 `customStorage`。

```ts title="auth.ts"
import { betterAuth } from "better-auth";

export const auth = betterAuth({
    //...其他选项
    rateLimit: {
        customStorage: {
            consume: async (key, rule) => {
                // atomically record one request for `key`
                // rule.window is in seconds, rule.max is the request limit
                return {
                    allowed: true,
                    retryAfter: null,
                };
            },
        },
    },
})
```

`customStorage.consume` 必须在一个操作中完成检查和递增。Better Auth 不再接受用于速率限制的独立 `get` 和 `set` 方法，因为这种形式可能导致并发请求通过同一个过期计数器。

## 处理速率限制错误 [#处理速率限制错误]

当请求超过速率限制时，Better Auth 会返回以下响应头：

* `X-Retry-After`：用户在多少秒后可以再次发起请求。

在客户端处理速率限制错误时，您可以全局处理或者针对单个请求分别处理。因为 Better Auth 客户端封装了 Better Fetch，您可以传递 `fetchOptions` 来处理速率限制错误。

**全局处理**

```ts title="auth-client.ts"
import { createAuthClient } from "better-auth/client";

export const authClient = createAuthClient({
    fetchOptions: {
        onError: async (context) => {
            const { response } = context;
            if (response.status === 429) {
                const retryAfter = response.headers.get("X-Retry-After");
                console.log(`超过速率限制。请在 ${retryAfter} 秒后重试`);
            }
        },
    }
})
```

**按请求处理**

```ts title="auth-client.ts"
import { authClient } from "./auth-client";

await authClient.signIn.email({
    fetchOptions: {
        onError: async (context) => {
            const { response } = context;
            if (response.status === 429) {
                const retryAfter = response.headers.get("X-Retry-After");
                console.log(`超过速率限制。请在 ${retryAfter} 秒后重试`);
            }
        },
    }
})
```

### 数据库表结构 [#数据库表结构]

如果您使用数据库存储速率限制数据，需要以下表结构：

表名：`rateLimit`



<DatabaseTable name="rateLimit" fields="rateLimitTableFields" />

