登录速率限制配置指南
本文档说明如何为 NEXTY.DEV 模板中的身份验证端点配置基于 IP 的速率限制。速率限制有助于防止滥用行为,如邮件垃圾、暴力破解攻击和过度使用免费套餐。
概述
NEXTY.DEV 默认使用 Better Auth 内置的速率限制功能,并可选地支持 Upstash Redis 以适配无服务器环境。
默认配置
lib/auth/index.ts
rateLimit: {
enabled: true,
window: 60, // 60 秒
max: 100, // 每个时间窗口 100 个请求(全局默认值)
customRules: {
"/sign-in/magic-link": { window: 60, max: 3 },
"/email-otp/send-verification-otp": { window: 60, max: 3 },
"/sign-in/email-otp": { window: 60, max: 5 },
},
}| 端点 | 时间窗口 | 最大请求数 | 说明 |
|---|---|---|---|
/sign-in/magic-link | 60秒 | 3 | Magic Link 登录邮件请求 |
/email-otp/send-verification-otp | 60秒 | 3 | OTP 验证码邮件请求 |
/sign-in/email-otp | 60秒 | 5 | OTP 验证码尝试次数 |
| 其他所有端点 | 60秒 | 100 | 全局默认值 |
自定义示例
场景 1:更严格的限制以防止免费套餐滥用
如果你担心用户通过切换账号来滥用免费套餐积分,可以添加更严格的每日限制:
lib/auth/index.ts
rateLimit: {
enabled: true,
window: 60,
max: 100,
customRules: {
// 严格的邮件发送限制
"/sign-in/magic-link": {
window: 60 * 60 * 24, // 24 小时
max: 5, // 每个 IP 每天只能发送 5 个魔法链接
},
"/email-otp/send-verification-otp": {
window: 60 * 60 * 24, // 24 小时
max: 10, // 每个 IP 每天只能发送 10 个 OTP 验证码
},
// 保持验证尝试在合理范围
"/sign-in/email-otp": {
window: 60 * 60 * 24, // 24 小时
max: 20, // 每 24 小时 20 次尝试
}
},
...(redis && {
customStorage: {
get: async (key: string) => {
const data = await redis!.get<{ key: string; count: number; lastRequest: number }>(key);
return data || undefined;
},
set: async (key: string, value: { key: string; count: number; lastRequest: number }) => {
await redis!.set(key, value, { ex: 60 * 60 * 24 }); // 24 小时后自动过期
},
},
}),
}场景 2:非常严格的防滥用(高价值服务)
对于免费套餐滥用成本较高的服务(如 AI 积分、API 调用):
rateLimit: {
enabled: true,
window: 60,
max: 50, // 降低全局默认值
customRules: {
// 极其严格的邮件限制
"/sign-in/magic-link": {
window: 60 * 60 * 24, // 24 小时
max: 3, // 每个 IP 每天只能发送 3 个魔法链接
},
"/email-otp/send-verification-otp": {
window: 60 * 60 * 24, // 24 小时
max: 5, // 每个 IP 每天只能发送 5 个 OTP 验证码
},
// 严格的验证限制
"/sign-in/email-otp": {
window: 60 * 60 * 24, // 24 小时
max: 20, // 每 24 小时 20 次尝试
},
},
...(redis && {
customStorage: {
get: async (key: string) => {
const data = await redis!.get<{ key: string; count: number; lastRequest: number }>(key);
return data || undefined;
},
set: async (key: string, value: { key: string; count: number; lastRequest: number }) => {
await redis!.set(key, value, { ex: 60 * 60 * 24 }); // 24 小时后自动过期
},
},
}),
}场景 3:宽松限制(内部工具 / 低风险)
对于内部工具或低风险应用程序:
rateLimit: {
enabled: true,
window: 60,
max: 200, // 提高全局默认值
customRules: {
"/sign-in/magic-link": { window: 60, max: 10 },
"/email-otp/send-verification-otp": { window: 60, max: 10 },
"/sign-in/email-otp": { window: 60, max: 20 },
},
}场景 4:禁用速率限制(仅限开发环境)
rateLimit: {
enabled: false, // ⚠️ 切勿在生产环境中这样做!
}存储选项
内存存储(默认)
没有你没有配置 Upstash Redis 环境变量,那么模板内置的 Redis 方法不会启用,这种情况速率限制数据将存储在内存中。
优点:
- 无需外部依赖
- 设置简单
缺点:
- 服务器重启后数据丢失
- 不能在多个实例之间共享,部署在 Vercel 等无服务器环境时,每个实例都有自己的内存,这会使速率限制失效
Upstash Redis 存储(生产环境推荐)
当配置了 UPSTASH_REDIS_REST_URL 和 UPSTASH_REDIS_REST_TOKEN 时,模板会自动使用 Redis。
lib/auth/index.ts
...(redis && {
customStorage: {
get: async (key: string) => {
const data = await redis!.get<{ key: string; count: number; lastRequest: number }>(key);
return data || undefined;
},
set: async (key: string, value: { key: string; count: number; lastRequest: number }) => {
await redis!.set(key, value, { ex: 120 }); // 120 秒后自动过期
},
},
}),优点:
- 数据在重启后保持
- 在所有实例之间共享
- 完美适配无服务器环境(Vercel、Cloudflare Workers)
- 通过 TTL 自动清理数据
缺点:
- 需要 Upstash 账号(有免费套餐)
设置 Upstash Redis
查看 Upstash 集成步骤。
数据库存储
提示
- 这种方案模板没有内置支持,如果你想用此方案,请根据文档自己实现。
- 数据库存储不会自动清理过期记录。你需要设置定时任务来清理旧记录。
Better Auth 也支持数据库存储:
lib/auth/index.ts
rateLimit: {
enabled: true,
storage: "database",
modelName: "rateLimit", // 表名
// ...
}然后运行迁移:
pnpm dlx @better-auth/cli migrate