Nginx limit_req 实战:CC 攻击防御完整配置方案

适用场景

当你的 Web 服务器遭遇 CC 攻击(Challenge Collapsar)时,攻击者通过大量高频 HTTP 请求耗尽服务器连接池和带宽资源,导致正常用户无法访问。本文介绍如何使用 Nginx 原生 limit_req 模块构建一套可落地的限频防御方案,适用于单机 Nginx 或 Nginx 前置反代架构。

前置条件

  • 操作系统:CentOS 7+ / Ubuntu 20.04+ / Debian 11+
  • Nginx 版本:≥ 1.18.0(需编译 --with-http_limit_req_module,主流发行版默认包含)
  • 权限:root 或 sudo 用户
  • 已安装 ab(Apache Bench)或 wrk 用于压测验证

执行以下命令确认模块可用:

nginx -V 2>&1 | grep -o 'http_limit_req_module'

输出 http_limit_req_module 即表示模块已启用。

原理说明

limit_req 模块基于 漏桶算法(Leaky Bucket)实现请求限速。核心机制如下:

  • 定义限速key:按客户端 IP($binary_remote_addr)或其他变量作为限速标识
  • 设置速率:如 10r/s 表示每秒最多允许 10 个请求通过
  • 突发缓冲burst 参数控制短时突发请求的缓冲队列大小
  • 无延迟模式nodelay 参数允许突发请求立即处理而不排队等待

当请求速率超过设定阈值时,Nginx 直接返回 503 Service Unavailable,在高频攻击场景下快速丢弃恶意请求,保护后端服务。

操作步骤

Step 1:定义限速区域

编辑 Nginx 主配置文件 /etc/nginx/nginx.conf,在 http 块内添加限速区域定义:

http {
    # ... 其他配置 ...

    # 限制每个IP每秒最多10个请求,缓冲区10MB
    limit_req_zone $binary_remote_addr zone=cc_defense:10m rate=10r/s;

    # 可选:按请求URI限速(针对特定接口)
    limit_req_zone $binary_remote_addr zone=api_limit:10m rate=5r/s;
}

参数说明:

  • $binary_remote_addr:使用二进制格式的客户端IP,比 $remote_addr 更节省内存
  • zone=cc_defense:10m:分配 10MB 共享内存,约可存储 16 万个 IP 记录
  • rate=10r/s:每秒允许 10 个请求(平均每 100ms 一个)

Step 2:在 Server/Location 中应用限速规则

编辑站点配置文件(如 /etc/nginx/conf.d/wafai.conf):

server {
    listen 443 ssl http2;
    server_name www.wafai.cn;

    # SSL 证书配置略

    # 全局限速(所有请求)
    location / {
        limit_req zone=cc_defense burst=20 nodelay;
        limit_req_status 503;

        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }

    # API 接口单独限速(更严格)
    location /wp-json/ {
        limit_req zone=api_limit burst=10 nodelay;
        limit_req_status 429;

        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }

    # 静态资源不限速
    location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff2?)$ {
        # 不设置 limit_req
        proxy_pass http://127.0.0.1:8080;
    }
}

关键参数:

  • burst=20:允许 20 个突发请求排队缓冲
  • nodelay:突发请求立即处理,不按速率延迟
  • limit_req_status 503:被限速时返回 503 状态码(默认也是 503)

Step 3:配置 IP 白名单(信任源跳过限速)

使用 geo 指令定义白名单,避免误限 CDN 回源 IP 或监控探针:

http {
    # 定义信任IP列表
    geo $limit_ip {
        default 1;          # 默认限速
        127.0.0.1 0;        # 本地不限速
        10.0.0.0/8 0;       # 内网不限速
        # CDN回源IP
        123.45.67.0/24 0;
    }

    # 用变量控制限速key:信任IP使用空值跳过限速
    map $limit_ip $limit_key {
        0 "";
        1 $binary_remote_addr;
    }

    limit_req_zone $limit_key zone=cc_defense:10m rate=10r/s;
}

这样配置后,白名单 IP 不会触发限速规则。

Step 4:添加自定义错误页面

为 503 状态码配置友好提示页面,避免用户体验生硬:

server {
    # ... 其他配置 ...

    error_page 503 = @cc_blocked;

    location @cc_blocked {
        default_type application/json;
        return 503 '{"code":503,"msg":"请求过于频繁,请稍后重试"}';
    }
}

Step 5:测试并重载配置

# 语法检查
nginx -t

# 平滑重载
nginx -s reload

配置验证

使用 ab 工具模拟高频请求,验证限速是否生效:

# 发送100个请求,并发20
ab -n 100 -c 20 https://www.wafai.cn/

# 预期结果:
# Complete requests:      100
# Failed requests:        80   ← 超出速率的请求被拒绝
# Non-2xx responses:      80   ← 返回503状态码

正常请求(低频)应全部返回 200:

# 每秒1个请求,共10个
for i in $(seq 1 10); do
    curl -s -o /dev/null -w "%{http_code}\n" https://www.wafai.cn/
    sleep 1
done
# 预期输出:10行 200

查看 Nginx 错误日志确认限速记录:

tail -f /var/log/nginx/error.log | grep "limiting requests"

# 预期输出示例:
# limiting requests, excess: 20.000 by zone "cc_defense", client: 1.2.3.4 ...

常见问题

Q1:burst 和 nodelay 怎么选?

burst 控制突发队列大小。不设 nodelay 时,突发请求会按设定速率依次处理(排队等待);设置 nodelay 后,突发请求立即处理但仍计入限速配额。对于 CC 防御场景,推荐使用 nodelay,避免正常用户排队等待。

Q2:站点使用 CDN,所有请求IP都是CDN回源IP,怎么限速?

需从 X-Forwarded-ForX-Real-IP 头中提取真实客户端 IP。在 http 块中添加:

set_real_ip_from 123.45.67.0/24;  # CDN回源IP段
real_ip_header X-Forwarded-For;
real_ip_recursive on;

这样 $binary_remote_addr 会自动替换为真实客户端 IP,限速即可按真实 IP 生效。

Q3:限速太严格导致正常用户被误杀怎么办?

调整策略:适当提高 rateburst 值;对静态资源路径不设限速;将 503 改为 429 状态码更符合 HTTP 语义;考虑结合 limit_conn(并发连接数限制)使用,而非仅依赖请求速率。

总结

核心要点回顾:

  • limit_req_zone 定义限速区域和速率,limit_req 在具体 location 中应用
  • burst + nodelay 组合兼顾灵活性与防护力度
  • geo + map 实现白名单豁免,避免误限 CDN 和内网
  • CDN 环境必须配合 set_real_ip_from 提取真实 IP
  • 配合 limit_conn(并发限制)和 WAF 规则可构建多层防御

延伸阅读:建议进一步了解 limit_conn 并发连接数限制、Nginx Lua 扩展动态限速、以及 Fail2ban 联动自动封禁恶意 IP 的方案,构建更完整的 CC 攻击防御体系。