nginx的limit_req_zone和limit_req用于单ip请求速率限制,必须在http块定义共享内存区并在location中启用;rate需按业务qps×1.5上浮,burst配合nodelay控制突发,$binary_remote_addr节省内存。

直接用 limit_req_zone 和 limit_req 控制单IP请求速率
Python后端本身不处理网络层限流,真正起效的是前置的Nginx。把限流逻辑放在应用层(比如用Flask写个装饰器)只会让攻击流量穿透到Python进程,提前耗尽CPU和连接数,反而加速崩溃。
必须在Nginx的http块中定义共享内存区,并在location里启用:
http {
limit_req_zone $binary_remote_addr zone=perip:10m rate=15r/s;
server {
location /api/ {
limit_req zone=perip burst=30 nodelay;
proxy_pass http://python_backend;
}
}
}
关键点:
-
rate=15r/s是保守起点,实际要按业务QPS×1.5再上浮——比如正常峰值是800 QPS,后端有4台机器,单机均值200,那rate至少设为300r/s,否则误杀合法用户 -
burst=30允许短时突发,但nodelay会让超出部分立刻返回503,不排队;去掉它会平滑延迟,但可能拖垮后端响应时间 - 别用
$remote_addr,必须用$binary_remote_addr——节省内存,1M能存约16000个IP,而文本形式只存约3000个
为什么 limit_conn_zone 比 limit_req_zone 更适合防SYN Flood类攻击
当攻击表现为大量半开TCP连接(如SYN Flood、Slowloris),limit_req完全无效——它只管HTTP请求,不管TCP握手。此时必须用limit_conn_zone限制并发连接数。
配置示例:
http {
limit_conn_zone $binary_remote_addr zone=conn_perip:10m;
server {
location / {
limit_conn conn_perip 10;
proxy_pass http://python_backend;
}
}
}
注意陷阱:
- 这个限制作用于Nginx worker进程级别,不是全局。如果Nginx开了4个worker,单IP最多能建40个连接,得配合
worker_connections整体调低 - 不要对
/static/或CDN回源路径启用limit_conn,否则CDN节点会被当成一个IP堵死 - 移动端App常复用连接,iOS/Android WebView也可能复用,
limit_conn值低于20容易导致白名单外的正常用户卡顿
WAF不能只靠User-Agent过滤,必须组合规则
单纯用if ($http_user_agent ~* "curl|wget") { return 403; } 已经失效。现在恶意爬虫和DDoS工具默认伪造Chrome UA,甚至带完整指纹。
真正有效的WAF规则要分层:
- 第一层:用
ngx_waf或Cloudflare WAF开启js_challenge,拦截无JS执行能力的客户端(99%的自动化工具过不去) - 第二层:对
/login、/search等高危路径加rate_based_statement,比如AWS WAF里设"Limit": 120(每分钟120次/IP) - 第三层:从Nginx日志提取高频IP,用
geo+map模块动态打标,再结合limit_req降级限速——比硬编码黑名单更可持续
示例动态标记:
geo $bad_ip {
default 0;
192.168.1.100 1;
203.0.113.42 1;
}
map $bad_ip $limit_key {
1 "$binary_remote_addr";
0 "";
}
limit_req_zone $limit_key zone=badip:1m rate=5r/s;
Python后端唯一该做的事:快速失败 + 日志可追溯
别试图在Django/Flask里做IP封禁或复杂限流。它的唯一职责是:收到Nginx转发的请求后,100ms内判断并返回,不参与任何防御决策。
实操建议:
- 所有接口加
@app.before_request钩子,记录request.headers.get("X-Real-IP")(确保Nginx透传)和request.user_agent.string,方便事后关联分析 - 遇到
429 Too Many Requests响应时,Python层不做重试,直接抛出异常并记录——避免重试放大攻击效果 - 禁用
DEBUG=True上线,关闭Django的ALLOWED_HOSTS宽松匹配,防止Host头攻击绕过WAF
最易被忽略的一点:Nginx的limit_req_status默认是503,但很多监控系统只告警5xx,结果攻击来了却没通知。务必显式设成limit_req_status 429;,和Python后端返回码对齐,才能进统一告警管道。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











