用 limit_req 防 cc 攻击需拦得准、反馈清、不露底:必须返回 429 状态码而非默认 503,配合 error_page 自定义简洁 json 响应;按路径分层配置 rate/burst/nodelay;通过 $limit_req_status 日志字段区分 rejected/passed/delayed 并分析高频攻击 ip;避免在 if 中使用、burst 过大或 zone 内存不足等陷阱。

用 limit_req 防 CC 攻击,关键不是“拦得狠”,而是“拦得准、反馈清、不露底”。核心在于把限流动作和 HTTP 状态码联动起来,让攻击者得不到有效响应,同时避免干扰真实用户。
必须返回 429 状态码,而不是默认 503
默认情况下,limit_req 超限时返回 503 Service Unavailable,这会暴露后端服务状态,还可能被爬虫或扫描器识别为“服务忙→可重试”。换成标准的 429 Too Many Requests 更专业:
- 在
http块中统一设置:limit_req_status 429; - 配合
error_page 429 @rate_limited;自定义响应体,比如返回简洁 JSON:{"code":429,"msg":"Too many requests"} - 不带 HTML 或调试信息,避免泄露 Nginx 版本、路径结构等敏感内容
按场景分层设置 rate + burst + nodelay 组合
单一全局限流容易误伤,应针对不同路径差异化配置:
-
登录接口:
rate=5r/m(每分钟 5 次),burst=3,nodelay—— 手动输入容错,但机器爆破立刻失败 -
API 接口:
rate=30r/m,burst=10,无nodelay—— 允许短时排队,防突发流量冲击 -
静态资源:可放宽至
rate=100r/m,burst=50,甚至关闭限流(配合 CDN 缓存)
用 $limit_req_status 日志字段做行为分析
在日志格式中加入该变量,能直观区分请求是否被限流:
- 定义日志格式:
log_format main '$remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" "$limit_req_status"'; - 日志中出现
rejected表示触发限流,passed表示放行,delayed表示排队处理 - 结合 ELK 或 Grafana,对
rejected高频 IP 做自动聚合,辅助判断是否需加入黑名单
避免常见配置陷阱
几个看似合理但实际削弱防护效果的操作:
- 不要在
if块里写limit_req—— Nginx 官方明确不支持,会导致规则失效 - 不要把
burst设得过大(如 >100)—— 失去限流意义,反而成为攻击缓冲区 - 不要忽略
zone内存大小 —— 10MB 约存 16 万个 IP,高并发站点需监控是否溢出(查 error log 中 “zone is full”)











