express-rate-limit vs rate-limiter-flexible vs express-brute vs express-limiter vs express-slow-down
Express.js 应用中的速率限制与暴力破解防护方案对比
express-rate-limitrate-limiter-flexibleexpress-bruteexpress-limiterexpress-slow-down类似的npm包:

Express.js 应用中的速率限制与暴力破解防护方案对比

express-bruteexpress-limiterexpress-rate-limitexpress-slow-downrate-limiter-flexible 都是用于保护 Node.js Express 应用免受暴力破解、API 滥用和 DDoS 攻击的中间件。它们通过限制特定 IP 或用户在一定时间内的请求频率来工作,但实现机制、存储后端支持和灵活性各不相同。express-rate-limit 是目前最流行且维护活跃的基础限流方案;rate-limiter-flexible 提供了最强大的跨框架支持和细粒度控制;express-brute 专注于暴力破解防护但已停止维护;express-limiter 是较早期的中间件实现;而 express-slow-down 则采用渐进式延迟策略而非直接拒绝请求。

npm下载趋势

3 年

GitHub Stars 排名

统计详情

npm包名称
下载量
Stars
大小
Issues
发布时间
License
express-rate-limit22,817,2453,231141 kB84 个月前MIT
rate-limiter-flexible1,774,6673,484198 kB1525 天前ISC
express-brute0568-219 年前BSD
express-limiter0423-218 年前MIT
express-slow-down029938.2 kB44 个月前MIT

Express.js 速率限制中间件深度对比:架构、性能与适用场景

在构建生产级 Express 应用时,保护 API 免受滥用和暴力破解是基本安全要求。本文深入对比五个主流限流中间件,从架构设计、存储支持、配置灵活性到实际代码实现,帮助你在技术选型时做出明智决策。

🛡️ 核心设计理念:直接拒绝 vs 渐进延迟 vs 暴力破解防护

不同包对"如何处理超限请求"有不同哲学。

express-rate-limit 采用标准的令牌桶或固定窗口算法,超限后直接返回 429 状态码。

// express-rate-limit: 直接拒绝超限请求
import rateLimit from 'express-rate-limit';

const limiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 分钟
  max: 100, // 每个 IP 最多 100 个请求
  message: '请求过多,请稍后再试'
});

app.use('/api/', limiter);

express-slow-down 不直接拒绝,而是随着请求频率增加逐渐延长响应时间。

// express-slow-down: 渐进式延迟
import slowDown from 'express-slow-down';

const speedLimiter = slowDown({
  windowMs: 15 * 60 * 1000,
  delayAfter: 50, // 50 个请求后开始延迟
  delayMs: (hits) => Math.min(hits * 100, 60000) // 每次增加 100ms,最多 60 秒
});

app.use('/api/', speedLimiter);

express-brute 专为登录等敏感端点设计,使用指数退避算法防止暴力破解。

// express-brute: 指数退避防护
import ExpressBrute from 'express-brute';
import MemStore from 'express-brute-mem-store';

const store = new MemStore();
const brute = new ExpressBrute(store);

app.post('/login', brute.prevent, (req, res) => {
  // 登录逻辑
  res.send('登录成功');
});

express-limiter 提供基础的 Redis 驱动限流,配置相对简单直接。

// express-limiter: Redis 基础限流
import limiter from 'express-limiter';

limiter(app, {
  lookup: 'connection.remoteAddress',
  total: 100,
  expire: 1000 * 60 * 60,
  redis: redisClient
});

rate-limiter-flexible 提供最细粒度的控制,支持按用户、IP、端点等多维度组合策略。

// rate-limiter-flexible: 细粒度策略
import { RateLimiterRedis } from 'rate-limiter-flexible';

const rateLimiter = new RateLimiterRedis({
  storeClient: redisClient,
  keyPrefix: 'middleware',
  points: 10,
  duration: 1
});

app.use('/api/', (req, res, next) => {
  rateLimiter.consume(req.ip)
    .then(() => next())
    .catch(() => res.status(429).send('Too Many Requests'));
});

💾 存储后端支持:内存 vs Redis vs 多后端

存储选择直接影响限流在集群环境下的有效性。

包名内存存储RedisMemcached数据库其他
express-brute✅ (需额外包)MongoDB (需额外包)
express-limiter
express-rate-limit自定义存储
express-slow-down依赖 express-rate-limit 存储
rate-limiter-flexible15+ 种存储

express-rate-limit 内置内存存储,也支持 Redis 通过 rate-limit-redis 包。

// express-rate-limit: Redis 存储配置
import RedisStore from 'rate-limit-redis';
import rateLimit from 'express-rate-limit';

const limiter = rateLimit({
  store: new RedisStore({
    sendCommand: (...args) => redisClient.call(...args),
  }),
  windowMs: 15 * 60 * 1000,
  max: 100
});

rate-limiter-flexible 支持最广泛的存储后端,包括 Redis、Memcached、MySQL、PostgreSQL、MongoDB 等。

// rate-limiter-flexible: Memcached 存储
import { RateLimiterMemcached } from 'rate-limiter-flexible';

const rateLimiter = new RateLimiterMemcached({
  storeClient: memcachedClient,
  points: 10,
  duration: 1
});

express-brute 需要为不同存储安装额外的 store 包。

// express-brute: MongoDB 存储
import ExpressBrute from 'express-brute';
import MongoDBStore from 'express-brute-mongodb-store';

const store = new MongoDBStore(mongoConnection);
const brute = new ExpressBrute(store);

🔑 键控策略:按 IP vs 按用户 vs 自定义

如何识别"谁"在发起请求决定了限流的精确度。

express-rate-limit 默认按 IP,但支持自定义键生成函数。

// express-rate-limit: 自定义键控
const limiter = rateLimit({
  keyGenerator: (req) => {
    return req.user ? req.user.id : req.ip;
  },
  max: 100
});

express-limiter 通过 lookup 配置指定键来源。

// express-limiter: 多键控支持
limiter(app, {
  lookup: ['connection.remoteAddress', 'user.id'],
  total: 100,
  expire: 3600000
});

rate-limiter-flexible 允许在 consume 时动态指定键,最灵活。

// rate-limiter-flexible: 动态键控
app.post('/api/action', async (req, res) => {
  const key = req.user ? `user:${req.user.id}` : `ip:${req.ip}`;
  
  try {
    await rateLimiter.consume(key);
    res.send('操作成功');
  } catch (rejRes) {
    res.status(429).send('操作过于频繁');
  }
});

express-brute 默认按 IP,可通过中间件参数覆盖。

// express-brute: 按用户限流
app.post('/login', brute.prevent, (req, res, next) => {
  // 验证逻辑
  if (valid) {
    brute.reset(req, next); // 成功后重置计数
  }
});

express-slow-down 继承 express-rate-limit 的键控逻辑。

// express-slow-down: 与 rate-limit 相同键控
const speedLimiter = slowDown({
  keyGenerator: (req) => req.user ? req.user.id : req.ip,
  delayAfter: 50,
  delayMs: 500
});

⚙️ 配置灵活性与高级功能

rate-limiter-flexible 支持最复杂场景,如不同端点不同限制、用户等级差异化限流。

// rate-limiter-flexible: 多级限流策略
const regularLimiter = new RateLimiterRedis({ points: 10, duration: 1 });
const premiumLimiter = new RateLimiterRedis({ points: 100, duration: 1 });

app.use('/api/', (req, res, next) => {
  const limiter = req.user?.isPremium ? premiumLimiter : regularLimiter;
  limiter.consume(req.ip).then(() => next()).catch(() => res.status(429).send());
});

express-rate-limit 支持请求头返回限流信息,便于客户端调整。

// express-rate-limit: 返回限流头
const limiter = rateLimit({
  standardHeaders: true, // 返回 RateLimit-* 头
  legacyHeaders: false,
  max: 100
});

express-slow-down 可配置延迟计算函数,实现非线性惩罚。

// express-slow-down: 非线性延迟
const speedLimiter = slowDown({
  delayMs: (hits) => Math.pow(hits, 2) * 100 // 指数增长延迟
});

express-brute 支持请求成功后自动重置计数,适合登录场景。

// express-brute: 成功重置
app.post('/login', brute.prevent, (req, res) => {
  authenticate(req.body, (err, user) => {
    if (user) {
      brute.reset(req); // 登录成功重置尝试次数
      res.send('欢迎');
    }
  });
});

express-limiter 配置相对固定,适合简单场景。

// express-limiter: 基础配置
limiter(app, {
  total: 100,
  expire: 3600000,
  lookup: 'connection.remoteAddress'
});

⚠️ 维护状态与生产建议

重要警告express-brute 已不再积极维护,最后更新距今较久。虽然功能稳定,但存在潜在安全风险,新项目中应避免使用。

express-limiter 维护频率较低,功能基础,适合简单 Redis 限流需求。

express-rate-limit 是目前 Express 生态中最活跃维护的限流包,社区支持好,文档完善。

express-slow-downexpress-rate-limit 同一作者维护,两者配合使用效果最佳。

rate-limiter-flexible 维护活跃,功能最强大,适合复杂企业级应用,但学习曲线稍陡。

📊 功能对比总结

特性express-bruteexpress-limiterexpress-rate-limitexpress-slow-downrate-limiter-flexible
维护状态⚠️ 停止维护🟡 低频维护🟢 活跃维护🟢 活跃维护🟢 活跃维护
存储支持有限Redis 仅内存 + Redis内存 + Redis15+ 种后端
配置灵活性极高
跨框架支持Express 仅Express 仅Express 仅Express 仅多框架支持
暴力破解防护✅ 专为设计✅ 可配置
渐进延迟✅ 核心功能✅ 可配置
学习曲线

💡 架构选型建议

场景 1:标准 REST API 限流

推荐express-rate-limit + Redis

大多数 API 只需要基础的请求频率限制,express-rate-limit 配置简单,社区支持好,足够满足需求。

import rateLimit from 'express-rate-limit';
import RedisStore from 'rate-limit-redis';

const apiLimiter = rateLimit({
  store: new RedisStore({ sendCommand: (...args) => redis.call(...args) }),
  windowMs: 15 * 60 * 1000,
  max: 100,
  standardHeaders: true
});

app.use('/api/', apiLimiter);

场景 2:登录端点暴力破解防护

推荐rate-limiter-flexible(新项目)或 express-brute(旧项目维护)

登录端点需要更严格的防护和指数退避策略。

import { RateLimiterRedis } from 'rate-limiter-flexible';

const loginLimiter = new RateLimiterRedis({
  storeClient: redisClient,
  keyPrefix: 'login',
  points: 5, // 5 次尝试
  duration: 60 * 15 // 15 分钟内
});

app.post('/login', async (req, res) => {
  try {
    await loginLimiter.consume(req.ip);
    // 验证逻辑
  } catch (rejRes) {
    res.status(429).send('尝试次数过多');
  }
});

场景 3:微服务架构多框架支持

推荐rate-limiter-flexible

如果你的应用不仅使用 Express,还涉及 Koa、Fastify 或其他框架,统一使用 rate-limiter-flexible 可以保持限流策略一致。

场景 4:需要保持服务可用性但减缓滥用

推荐express-rate-limit + express-slow-down 组合

先使用 express-slow-down 减缓频繁请求者,达到硬性限制后再用 express-rate-limit 拒绝。

import slowDown from 'express-slow-down';
import rateLimit from 'express-rate-limit';

const speedLimiter = slowDown({
  windowMs: 15 * 60 * 1000,
  delayAfter: 50,
  delayMs: 500
});

const rateLimiter = rateLimit({
  windowMs: 15 * 60 * 1000,
  max: 100
});

app.use('/api/', speedLimiter, rateLimiter);

场景 5:多租户 SaaS 应用差异化限流

推荐rate-limiter-flexible

需要根据用户订阅等级动态调整限流策略。

const limiters = {
  free: new RateLimiterRedis({ points: 10, duration: 1 }),
  pro: new RateLimiterRedis({ points: 100, duration: 1 }),
  enterprise: new RateLimiterRedis({ points: 1000, duration: 1 })
};

app.use('/api/', (req, res, next) => {
  const tier = req.user?.subscription || 'free';
  limiters[tier].consume(req.user.id)
    .then(() => next())
    .catch(() => res.status(429).send('超出配额'));
});

🎯 最终建议

对于新项目,优先选择 express-rate-limit(简单场景)或 rate-limiter-flexible(复杂场景)。两者维护活跃,文档完善,社区支持好。

对于旧项目维护,如果已使用 express-bruteexpress-limiter 且运行稳定,可暂时保留,但建议制定迁移计划。

express-slow-down 是很好的补充工具,与 express-rate-limit 配合使用可提供更友好的限流体验。

记住:限流只是安全策略的一环,应结合认证、授权、输入验证等措施构建完整的防护体系。

如何选择: express-rate-limit vs rate-limiter-flexible vs express-brute vs express-limiter vs express-slow-down

  • express-rate-limit:

    选择它如果你需要简单、轻量且广泛采用的 Express 专用限流中间件。它支持内存和 Redis 存储,配置简单,适合大多数标准 API 限流场景。社区活跃,文档完善,是 Express 生态中的默认选择。

  • rate-limiter-flexible:

    选择它如果你需要跨框架支持(不仅限于 Express)、细粒度的限流策略或多种存储后端(Redis、Memcached、数据库等)。它提供最灵活的配置选项,适合复杂的企业级应用和微服务架构。

  • express-brute:

    仅建议在维护旧项目时使用,该包已不再积极维护。它专为防止暴力破解设计,支持指数退避策略,但缺乏现代存储后端支持。如果是新项目,请避免使用并考虑迁移到 rate-limiter-flexible

  • express-limiter:

    适用于需要简单 Redis 集成的传统 Express 项目。它的配置相对直接,但功能较为基础,缺乏灵活的键控策略。如果你的项目已经依赖它且运行稳定,可以继续使用,但新项目建议评估更现代的替代方案。

  • express-slow-down:

    选择它当你希望在不直接拒绝请求的情况下减缓恶意流量。它通过增加响应延迟来惩罚频繁请求者,适合需要保持服务可用性但希望 discouraging 滥用的场景。通常与 express-rate-limit 配合使用。

express-rate-limit的README

express-rate-limit

tests npm version npm downloads license

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.

Usage

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)

Data Stores

The rate limiter comes with a built-in memory store, and supports a variety of external data stores.

Configuration

All function options may be async. Click the name for additional info and default values.

OptionTypeRemarks
windowMsnumberHow long to remember requests for, in milliseconds.
limitnumber | functionHow many requests to allow.
messagestring | json | functionResponse to return after limit is reached.
statusCodenumberHTTP status code after limit is reached (default is 429).
handlerfunctionFunction to run after limit is reached (overrides message and statusCode settings, if set).
legacyHeadersbooleanEnable the X-Rate-Limit header.
standardHeaders'draft-6' | 'draft-7' | 'draft-8'Enable the Ratelimit header.
identifierstring | functionName associated with the quota policy enforced by this rate limiter.
storeStoreUse a custom store to share hit counts across multiple nodes.
passOnStoreErrorbooleanAllow (true) or block (false, default) traffic if the store becomes unavailable.
keyGeneratorfunctionIdentify users (defaults to IP address).
ipv6Subnetnumber (32-64) | function | falseHow many bits of IPv6 addresses to use in default keyGenerator
requestPropertyNamestringAdd rate limit info to the req object.
skipfunctionReturn true to bypass the limiter for the given request.
skipSuccessfulRequestsbooleanUncount 1xx/2xx/3xx responses.
skipFailedRequestsbooleanUncount 4xx/5xx responses.
requestWasSuccessfulfunctionUsed by skipSuccessfulRequests and skipFailedRequests.
validateboolean | objectEnable or disable built-in validation checks.

Thank You

Sponsored by Zuplo a fully-managed API Gateway for developers. Add dynamic rate-limiting, authentication and more to any API in minutes. Learn more at zuplo.com

zuplo-logo


Thanks to Mintlify for hosting the documentation at express-rate-limit.mintlify.app

Create your docs today


Finally, thank you to everyone who's contributed to this project in any way! 🫶

Issues and Contributing

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!

License

MIT © Nathan Friedly, Vedant K