遇到 Nginx 因 limit_req 返回 503,通常意味着限流策略触发了拒绝机制,优化方向不是关闭限流,而是调整速率阈值和突发容忍度,同时区分正常流量与攻击特征。
先说结论:503 是限流生效的正常表现,优化重点在于调整 burst 值和识别误伤,而非单纯消除报错
- 先判断:确认 503 是来自 limit_req 模块而非后端服务不可用
- 优先做:调整 burst 参数允许突发流量,避免正常用户被误拦
- 再验证:通过 access 日志观察限流触发频率和状态码分布
命令速用版(含白名单配置)
以下配置展示了如何结合 map 模块实现 IP 白名单,并将限流作用域缩小到特定接口,避免全局误伤。
http {
# 1. 定义白名单逻辑:可信 IP 的限流 key 设为空字符串
map $remote_addr $limit_key {
default $binary_remote_addr;
192.168.1.0/24 "";
10.0.0.1 "";
}
# 2. 使用变量作为 zone 的 key
limit_req_zone $limit_key zone=one:10m rate=10r/s;
server {
# 3. 建议只对动态接口限流,避免影响静态资源
location /api/ {
limit_req zone=one burst=20 nodelay;
limit_req_status 429;
}
}
}注意:尽量避免在 location / 全局开启严格限流,否则可能误伤所有接口包括静态资源。白名单 IP 的 key 设为空字符串 "" 可使其绕过限流。
为什么会这样
limit_req 模块使用漏桶算法(leaky bucket algorithm)控制请求速率。当请求超过设定的 rate 且超出 burst 缓冲容量时,Nginx 会直接拒绝请求。默认情况下,被拒绝的请求会返回 503 Service Unavailable 状态码。这在防御 CC 攻击时是预期行为,但如果配置过严,正常用户高峰访问也会触发 503。
分步处理与 CC 特征识别
1. 确认报错来源:检查 error.log,搜索 limit_req 关键字,确认 503 是由限流模块产生,而不是 upstream 后端服务返回。
2. 调整突发容量:在 limit_req 指令中增加 burst 参数。例如 rate=10r/s 时,设置 burst=20,允许短时间内突发请求排队处理。
3. 优化处理策略:添加 nodelay 参数,让 burst 内的请求立即处理而不是延迟,减少用户等待感。
4. 自定义状态码:使用 limit_req_status 指令将 503 改为 429 Too Many Requests,便于监控区分是限流还是服务故障。
5. 识别 CC 攻击特征:正常用户通常有完整的 User-Agent 且访问频率分散,CC 攻击往往 UA 缺失或单一 IP 高频访问。可结合日志分析:
grep "limit_req" /var/log/nginx/error.log | awk '{print $8}' | sort | uniq -c | sort -nr压测验证:具体的 curl 脚本
使用以下 shell 脚本模拟高频请求,观察返回状态码。如果配置生效,超过 burst 限制的请求应返回 429。
for i in {1..50}; do curl -I -o /dev/null -s -w "%{http_code}\n" http://your-domain.com/api/test; done同时 tail -f access.log,查看状态码列是否出现 429 或 503。如果调整 burst 后,正常访问不再出现限流状态码,而高频攻击请求仍被拦截,说明配置生效。
常见坑
1. NAT 环境误伤:多个用户共用出口 IP 时,严格限流会导致正常用户无法访问,需结合 cookie 挑战或_js 验证。
2. 静态资源限流:对 css、js、图片等静态文件开启严格限流会影响页面加载,建议只对动态接口启用。
3. 状态码语义:改为 429 虽便于识别,但搜索引擎或部分客户端可能仍期待 503,修改前需确认业务兼容性。
4. 内存消耗:limit_req_zone 需要共享内存,zone 大小设置过小可能导致无法记录足够 IP,设置过大浪费内存。
5. 白名单配置错误:确保 map 模块定义在 http 块内,且空字符串使用双引号包裹,否则可能生效失败。
参考来源
1. Nginx 官方文档 - ngx_http_limit_req_module
URL: http://nginx.org/en/docs/http/ngx_http_limit_req_module.html