Nginx 层限流是防止 CC 攻击 Django 应用的首选方案,适用于 Nginx 作为反向代理直接暴露公网 IP 的场景,配置错误可能导致正常用户被误拦截。
先说结论:在 Nginx 配置文件中定义限流_zone_并在 location 块启用,比在 Django 代码层处理更能节省服务器资源。
- 先判断:确认 Nginx 是否直接接收公网流量,若前方还有负载均衡器需透传真实 IP。
- 优先做:配置 limit_req_zone 限制单 IP 请求频率,设置合理的 burst 缓冲值。
- 再验证:使用压测工具触发限流,观察是否返回 503 状态码且 Django 日志无异常增长。
命令速用版
以下配置片段可直接放入 Nginx 配置文件的 http 块和 server 块中,用于启用基础限流。
http {
# 定义限流区域,内存 10MB,每秒允许 1 个请求
limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;
server {
location / {
# 应用限流,burst 允许突发 5 个请求,nodelay 不延迟处理
limit_req zone=one burst=5 nodelay;
proxy_pass http://127.0.0.1:8000;
}
}
}为什么会这样
Nginx 限流能在请求到达 Django 应用之前拦截恶意流量,避免 Python 进程被无效请求占满。
CC 攻击的本质是消耗服务器处理请求的资源。Django 运行在 Gunicorn 或 uWSGI 之上,每个请求都需要启动 Python 解释器、加载框架代码、查询数据库。如果在 Django 中间件层做限流,请求已经消耗了 WSGI 服务器的连接资源和部分 CPU 周期。Nginx 作为反向代理,位于请求链路的最前端,使用 C 语言编写,处理连接的开销极小。通过在 Nginx 层丢弃超额请求,可以保护后端的 Django 应用不被打挂。
分步处理
按顺序修改 Nginx 配置并 reload,确保每一步都有检查点。
步骤 1:编辑 Nginx 配置文件
找到主配置文件,通常位于/etc/nginx/nginx.conf 或/etc/nginx/conf.d/下的站点配置。在 http 块中添加 limit_req_zone 指令。变量建议使用$binary_remote_addr 而不是$remote_addr,前者占用内存更少。
步骤 2:在 location 块启用限流
在需要保护的 location 块中添加 limit_req 指令。burst 参数允许请求在队列中等待,nodelay 参数表示队列中的请求立即处理,超过 burst 部分的请求直接拒绝。对于 API 接口,rate 可以设低一些;对于普通网页,rate 可以适当放宽。
步骤 3:检查配置语法
执行命令nginx -t。如果输出 syntax is ok 和 test is successful,说明配置无误。如果报错,检查大括号匹配和分号是否遗漏。
步骤 4:重载 Nginx
执行命令nginx -s reload。不要使用 restart,以免中断现有连接。观察进程是否存活,确认没有平滑重启失败。
怎么验证是否生效
通过模拟高频请求观察 HTTP 状态码和后端日志,确认限流规则正在工作。
方法 1:使用 curl 循环请求
在终端运行循环命令,快速发送超过 rate 设定的请求数。观察响应头,超过限制的请求应返回 503 Service Temporarily Unavailable 状态码。
方法 2:检查 Nginx 错误日志
查看/etc/nginx/logs/error.log 或/var/log/nginx/error.log。如果限流生效,日志中会出现 limiting requests, excess: 1.234 by zone 类似的记录。这条日志证明 Nginx 主动拦截了请求。
方法 3:监控 Django 日志
观察 Django 应用的访问日志或 stdout。在压测期间,Django 接收到的请求数量应明显少于 Nginx 接收到的总请求数。如果两者持平,说明限流未生效或配置位置错误。
常见坑
配置限流时容易忽略真实 IP 获取和突发流量处理,导致误杀或失效。
1. 前方有负载均衡器时 IP 获取错误
如果 Nginx 前方还有 SLB 或 CDN,$remote_addr 获取的是负载均衡器的 IP,导致所有用户共用一个限流配额。需在 Nginx 中配置 set_real_ip_from 和 real_ip_header,确保$binary_remote_addr 获取的是用户真实 IP。
2. burst 设置过小导致正常用户受阻
现代浏览器打开一个页面可能并发加载多个静态资源。如果 rate 设为 1r/s 且 burst 为 0,页面资源会加载失败。建议根据页面资源数量调整 burst 值,或对静态文件 location 单独放宽限制。
3. 限流后未自定义错误页面
默认 503 页面对用户不友好。可配置 limit_req_status 429 或配合 error_page 指令返回友好的 JSON 提示或 HTML 页面,避免用户以为网站崩溃。
常见问题
Django 中间件限流和 Nginx 限流选哪个?
优先选 Nginx 限流,因为它在请求进入 Python 环境之前就拦截,资源消耗更低。
限流后返回 503 状态码正常吗?
正常,这是 Nginx 默认的被限流响应码,代表服务器暂时无法处理该请求。
如何防止限流误伤正常用户?
通过设置合理的 burst 缓冲值,并将可信 IP 段加入限流白名单 ignore 规则。
参考来源
- Nginx Official Documentation - Limiting Request Rate (ngx_http_limit_req_module): https://nginx.org/en/docs/http/ngx_http_limit_req_module.html
- Django Documentation - How to deploy Django with WSGI: https://docs.djangoproject.com/en/stable/howto/deployment/wsgi/