express-rate-limit 是标准的请求频率限制中间件,适用于大多数通用场景。express-slow-down 通常与前者配合使用,用于在达到限制前逐渐延迟响应而非直接阻断。rate-limiter-flexible 提供更细粒度的控制和多种存储后端支持,适合复杂架构。express-brute 是早期的暴力破解防护库,但目前已不再维护,建议在新项目中避免使用。
在构建 Node.js 后端服务时,保护 API 免受滥用和暴力破解攻击是架构设计中的关键一环。express-brute、express-rate-limit、express-slow-down 和 rate-limiter-flexible 都是用于解决这一问题的中间件,但它们的设计理念、维护状态和适用场景有很大不同。本文将从技术实现、存储支持和维护状态三个维度进行深度对比。
这四个包的核心目标都是控制请求频率,但初始化的方式和配置项有所不同。
express-brute 采用类实例化的方式,通常用于特定路由(如登录接口)的暴力破解防护。
// express-brute
const ExpressBrute = require('express-brute');
const store = new ExpressBrute.MemoryStore();
const bruteforce = new ExpressBrute(store);
app.post('/login', bruteforce.prevent, (req, res) => {
// 处理登录逻辑
});
express-rate-limit 作为全局或路由中间件使用,配置直观,专注于窗口期内的最大请求数。
// express-rate-limit
const rateLimit = require('express-rate-limit');
const limiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 100
});
app.use(limiter);
express-slow-down 配置类似 express-rate-limit,但重点在于设置延迟阈值,而非直接阻断。
// express-slow-down
const slowDown = require('express-slow-down');
const speedLimiter = slowDown({
windowMs: 15 * 60 * 1000,
delayAfter: 50,
delayMs: (hits) => hits * 100
});
app.use(speedLimiter);
rate-limiter-flexible 需要实例化限制器对象,并配合中间件包装器使用,配置更为灵活。
// rate-limiter-flexible
const { RateLimiterMemory, RateLimiterExpress } = require('rate-limiter-flexible');
const rateLimiter = new RateLimiterMemory({
points: 10,
duration: 1
});
app.use(new RateLimiterExpress(rateLimiter).middleware);
存储后端决定了限制数据保存在哪里,直接影响系统的扩展性和性能。
express-brute 支持内存和 Redis,但配置方式较为老旧,扩展性有限。
// express-brute
const RedisStore = require('express-brute-redis');
const store = new RedisStore({
host: 'localhost',
port: 6379
});
express-rate-limit 默认使用内存,但官方支持多种外部存储插件(如 Redis、MongoDB)。
// express-rate-limit
const RedisStore = require('rate-limit-redis');
const limiter = rateLimit({
store: new RedisStore({
sendCommand: (...args) => redisClient.call(...args),
}),
windowMs: 15 * 60 * 1000,
max: 100
});
express-slow-down 通常复用 express-rate-limit 的存储配置,两者常一起工作。
// express-slow-down
const RedisStore = require('rate-limit-redis');
const speedLimiter = slowDown({
store: new RedisStore({
sendCommand: (...args) => redisClient.call(...args),
}),
windowMs: 15 * 60 * 1000
});
rate-limiter-flexible 原生支持最广泛的存储后端,包括 Redis、Memcached、Postgres、Mongo 等。
// rate-limiter-flexible
const { RateLimiterRedis } = require('rate-limiter-flexible');
const rateLimiter = new RateLimiterRedis({
storeClient: redisClient,
points: 10,
duration: 1
});
如何识别用户(IP、UserID、API Key)决定了防护的精准度。
express-brute 默认基于 IP,自定义键需要通过中间件逻辑手动处理,较为繁琐。
// express-brute
app.post('/login', (req, res, next) => {
bruteforce.handle(new ExpressBrute.Request(req, res), next, {
id: req.body.username // 手动指定键
});
}, (req, res) => {
// 登录成功
});
express-rate-limit 通过 keyGenerator 配置项轻松自定义键,支持基于用户 ID 等逻辑。
// express-rate-limit
const limiter = rateLimit({
keyGenerator: (req) => {
return req.user.id || req.ip;
}
});
express-slow-down 同样支持 keyGenerator,逻辑与 express-rate-limit 一致。
// express-slow-down
const speedLimiter = slowDown({
keyGenerator: (req) => {
return req.user.id || req.ip;
}
});
rate-limiter-flexible 在中间件配置中直接支持 getKey,且支持多维度组合键,灵活性最高。
// rate-limiter-flexible
app.use(new RateLimiterExpress(rateLimiter, {
getKey: (req) => {
return `${req.user.id}_${req.ip}`;
}
}).middleware);
选择库时,维护状态直接关系到安全性。过时的库可能包含未修复的漏洞。
express-brute 已多年未更新,社区普遍认为其不再适合新项目。存在潜在的安全风险,不建议继续使用。
// express-brute
// ⚠️ 警告:该库已不再维护,npm 页面无近期更新
// 建议迁移至 rate-limiter-flexible
express-rate-limit 维护活跃,定期更新以适应 Express 新版本和安全需求。
// express-rate-limit
// ✅ 状态:活跃维护
// 适合大多数标准场景
express-slow-down 维护活跃,通常与 express-rate-limit 同步更新。
// express-slow-down
// ✅ 状态:活跃维护
// 适合作为补充策略
rate-limiter-flexible 维护活跃,专注于提供企业级的灵活性和安全性。
// rate-limiter-flexible
// ✅ 状态:活跃维护
// 适合复杂和高并发场景
| 特性 | express-brute | express-rate-limit | express-slow-down | rate-limiter-flexible |
|---|---|---|---|---|
| 维护状态 | ❌ 不再维护 | ✅ 活跃 | ✅ 活跃 | ✅ 活跃 |
| 主要用途 | 暴力破解防护 | 通用频率限制 | 响应延迟 throttling | 复杂频率限制 |
| 存储支持 | 有限 (Redis/Mem) | 多 (通过插件) | 多 (同 rate-limit) | 极多 (原生支持) |
| 配置复杂度 | 中 | 低 | 低 | 中高 |
| 推荐场景 | 旧项目迁移 | 新项目默认选择 | 用户体验优化 | 分布式/复杂系统 |
express-rate-limit 是大多数项目的默认选择 🧰。它简单、可靠,足以应对 90% 的 API 防护需求。配合 express-slow-down 可以在不阻断用户的情况下缓解流量峰值。
rate-limiter-flexible 是复杂系统的首选 🔧。如果你需要基于数据库用户 ID 进行限制,或者需要在 Redis 集群中共享限制状态,它的灵活性和存储支持是无与伦比的。
express-brute 应被视作技术债务 🗑️。虽然它曾经很流行,但现在的替代品更安全、更灵活。如果在新项目中看到它,请务必替换。
最终建议:对于新项目,优先使用 express-rate-limit。如果业务逻辑复杂或需要分布式支持,选择 rate-limiter-flexible。永远不要在新代码中引入 express-brute。
选择 express-rate-limit 如果你需要快速搭建标准的频率限制功能。它配置简单,社区支持好,适合大多数 API 防护场景,尤其是只需要基于 IP 或简单键值进行限制的情况。
不建议在新项目中使用。该库已多年未更新,存在潜在的安全维护风险。如果你的旧项目正在使用它,建议尽快迁移到 rate-limiter-flexible 或 express-rate-limit 以获得持续的安全补丁和支持。
选择 express-slow-down 当你希望改善用户体验而不是直接返回 429 错误。它适合配合 express-rate-limit 使用,在用户接近限制阈值时逐渐增加响应延迟,给合法用户缓冲空间。
选择 rate-limiter-flexible 如果你需要复杂的限制逻辑,比如基于用户 ID、API 密钥或多维度组合键。它支持多种存储后端(Redis、MongoDB 等),适合高并发或分布式系统架构。
express-rate-limit Basic rate-limiting middleware for Express. Use to limit repeated requests to public APIs and/or endpoints such as password reset. Plays nice with express-slow-down and ratelimit-header-parser.
The full documentation is available on-line.
import { rateLimit } from 'express-rate-limit'
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
limit: 100, // Limit each IP to 100 requests per `window` (here, per 15 minutes).
standardHeaders: 'draft-8', // draft-6: `RateLimit-*` headers; draft-7 & draft-8: combined `RateLimit` header
legacyHeaders: false, // Disable the `X-RateLimit-*` headers.
ipv6Subnet: 56, // Set to 60 or 64 to be less aggressive, or 52 or 48 to be more aggressive
// store: ... , // Redis, Memcached, etc. See below.
})
// Apply the rate limiting middleware to all requests.
app.use(limiter)
The rate limiter comes with a built-in memory store, and supports a variety of external data stores.
All function options may be async. Click the name for additional info and default values.
| Option | Type | Remarks |
|---|---|---|
windowMs | number | How long to remember requests for, in milliseconds. |
limit | number | function | How many requests to allow. |
message | string | json | function | Response to return after limit is reached. |
statusCode | number | HTTP status code after limit is reached (default is 429). |
handler | function | Function to run after limit is reached (overrides message and statusCode settings, if set). |
legacyHeaders | boolean | Enable the X-Rate-Limit header. |
standardHeaders | 'draft-6' | 'draft-7' | 'draft-8' | Enable the Ratelimit header. |
identifier | string | function | Name associated with the quota policy enforced by this rate limiter. |
store | Store | Use a custom store to share hit counts across multiple nodes. |
passOnStoreError | boolean | Allow (true) or block (false, default) traffic if the store becomes unavailable. |
keyGenerator | function | Identify users (defaults to IP address). |
ipv6Subnet | number (32-64) | function | false | How many bits of IPv6 addresses to use in default keyGenerator |
requestPropertyName | string | Add rate limit info to the req object. |
skip | function | Return true to bypass the limiter for the given request. |
skipSuccessfulRequests | boolean | Uncount 1xx/2xx/3xx responses. |
skipFailedRequests | boolean | Uncount 4xx/5xx responses. |
requestWasSuccessful | function | Used by skipSuccessfulRequests and skipFailedRequests. |
validate | boolean | object | Enable or disable built-in validation checks. |
logger | Logger | Custom logger |
Thanks to Mintlify for hosting the documentation at express-rate-limit.mintlify.app
And thank you to everyone who's contributed to this project in any way! 🫶
If you encounter a bug or want to see something added/changed, please go ahead and open an issue! If you need help with something, feel free to start a discussion!
If you wish to contribute to the library, thanks! First, please read the contributing guide. Then you can pick up any issue and fix/implement it!
MIT © Nathan Friedly, Vedant K