Express.js 限流实战:保护你的 API
限流是 API 安全的基础设施。本文实战 express-rate-limit:基础配置、分级限流策略、登录防爆破专项与生产部署要点。
npm install express-rate-limit,rateLimit({ windowMs: 15*60*1000, max: 100 }) 即完成基础限流。生产三要素:按端点敏感度分级限流(登录接口最严)、store 换 Redis 支持多实例、配合 trust proxy 正确获取真实 IP。
基础配置
npm install express-rate-limit
const rateLimit = require('express-rate-limit');
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 分钟窗口
max: 100, // 每 IP 最多 100 次
standardHeaders: true, // RateLimit-* 响应头
legacyHeaders: false,
message: { error: '请求过于频繁,请稍后再试' },
});
app.use(limiter);
分级限流策略
一刀切不是好策略——按端点敏感度分级:
// 登录/注册:最严(防爆破)
const authLimiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 5,
skipSuccessfulRequests: true, // 登录成功不计数——只算失败尝试
});
app.use('/auth/login', authLimiter);
// 写操作:中等
const writeLimiter = rateLimit({ windowMs: 60 * 1000, max: 20 });
app.use('/api/posts', writeLimiter);
// 读操作:宽松
const readLimiter = rateLimit({ windowMs: 60 * 1000, max: 300 });
app.use('/api', readLimiter);
用户分级与自定义键
const tieredLimiter = rateLimit({
windowMs: 60 * 1000,
max: (req) => req.user?.plan === 'premium' ? 1000 : 60, // 按套餐分级
keyGenerator: (req) => req.user?.id || req.ip, // 登录用户按 ID 限
});
按用户 ID 限流避免"公司出口 NAT 全公司共用一个 IP"误伤。
生产部署要点
- 多实例必须共享存储:默认内存存储各实例独立计数——换
rate-limit-redis让计数全局一致; - trust proxy:Nginx/负载均衡后,
app.set('trust proxy', 1)才能拿到真实客户端 IP; - 超限的可观测:429 响应要记录日志——攻击与误伤都靠日志复盘;
- 配合 WAF/CDN:应用层限流是第二道防线,大流量攻击靠 CDN 边缘层先挡。
检查限流是否误伤正常请求
429 增多既可能是异常流量,也可能是额度过小或客户端重试失控。把路由模板、限流结果、可信代理解析后的来源与请求耗时写入脱敏日志,通过观测云检查分布,再结合 APM 判断后端是否真的饱和。限流日志不能单独证明攻击,告警应结合业务基线、来源分布和受影响请求数量。
接入文档:日志采集 · Node.js APM 接入 · 监控器与告警策略。
常见问题(FAQ)
Q:限流返回 429 之外还能怎么做?
429 + Retry-After 头是标准;对可疑流量也可降级响应(返回缓存数据)或引入验证码挑战——完全拒绝不是唯一选项。
Q:滑动窗口和固定窗口的区别?
express-rate-limit 默认固定窗口(窗口边界可瞬时双倍流量);严格平滑用滑动窗口(rate-limiter-flexible 库支持)。常规防护固定窗口足够。
Q:怎么防分布式慢速攻击?
应用层限流对低速分布式攻击乏力——结合行为特征(UA、请求路径模式)分析,必要时上 WAF。观测数据是识别这类攻击的前提。
参考文档
本文依据公开文档整理,示例需按项目依赖版本验证;不将文档中的性能描述视为本项目实测结果。