防 CC 的核心思路其实就两条:把单个 IP 同时挂在服务器上的连接数压下去,再把它的请求频率压下去。Nginx 自带的 limit_conn 和 limit_req 两个模块就能干这事,不需要额外装东西。下面直接给配置和验证方法。
一、先确认模块,再定义共享内存区
执行 nginx -V 2>&1 | grep -o 'with-http_limit_conn_module|with-http_limit_req_module',两个都出现说明可用。绝大多数发行版编译时默认带上。
然后定义限流用的共享内存区,放在 http 块里:
http {
# 以客户端 IP 为 key,记录并发连接数,10MB 约存 16 万个 IP
limit_conn_zone $binary_remote_addr zone=perip_conn:10m;
# 以客户端 IP 为 key,记录请求速率,10MB 同样约 16 万个 IP
limit_req_zone $binary_remote_addr zone=perip_req:10m rate=10r/s;
}
注意用的是 $binary_remote_addr 而不是 $remote_addr,前者是二进制定长(IPv4 占 4 字节),内存占用小得多,别写错。
二、在 server / location 里落地限制
把限制加到需要保护的站点或接口上:
server {
listen 80;
server_name example.com;
# 单 IP 最多 20 个并发连接
limit_conn perip_conn 20;
# 单 IP 请求速率 10r/s,突发队列 20,超出直接拒绝
limit_req zone=perip_req burst=20 nodelay;
location / {
root /var/www/html;
}
# 静态资源可以放宽,避免误伤正常用户
location ~* .(jpg|png|css|js)$ {
limit_conn perip_conn 50;
limit_req zone=perip_req burst=50 nodelay;
}
}
几个参数说清楚:burst=20 是允许瞬时排队 20 个请求,nodelay 表示这 20 个立即放行而不按 rate 平滑处理,否则用户会感觉明显卡顿。如果你要严格限速,就去掉 nodelay,但要接受延迟。
被拒绝时默认返回 503。想改成 429 更语义化,加一行:
limit_req_status 429;
limit_conn_status 429;
三、日志与验证
日志级别调到 warn 才能看到限流记录:
# 在 http 块中
limit_req_log_level warn;
limit_conn_log_level warn;
# 在对应 server 的 access_log 中加变量
log_format limit '$remote_addr - $status req=$request reqtime=$request_time limit=$limit_req_status';
access_log /var/log/nginx/limit.log limit;
验证用 ab 或 wrk 压一下:
ab -n 200 -c 50 http://example.com/
如果配置生效,日志里会出现 429(或 503),且 error.log 有 “limiting requests” 字样。压测时注意别从本机压本机,容易把 SSH 也限了,建议从另一台机器发起。
四、防 CC 的补充建议
- 限流阈值别一刀切,先观察正常用户峰值再定。rate 设太小,公司出口 IP 下的员工会集体被限。
- 对登录、短信验证码这类接口单独收紧,比如
limit_req zone=perip_req burst=5 nodelay;,比全站统一值更有效。 - 结合
map把已知搜索引擎 IP 段排除,避免误伤收录。 - Nginx 限流是单机维度的,多台后端要各自配,或者在前置 LB 上统一做。真正的 CC 攻击建议再叠加云 WAF 或 CDN 的 IP 信誉库。
- 改完配置先
nginx -t再nginx -s reload,别直接重启。

