Nginx 访问日志可以帮助发现某个来源短时间内发起大量请求,但“请求多”不等于恶意。共享出口、搜索引擎、监控和前端资源加载,都可能产生高访问量。先统计候选来源,再结合业务规则处置,比直接把 IP 写入防火墙更容易控制误伤。
将统计窗口固定下来
下面使用 Python 3,读取一份已经按时间范围切好的日志。假设每行首字段为客户端 IP,这需要与实际 Nginx log_format 对应。脚本只输出达到阈值的候选,不修改系统防火墙:
import argparse
import ipaddress
from collections import Counter
parser = argparse.ArgumentParser()
parser.add_argument("logfile")
parser.add_argument("--threshold", type=int, default=500)
args = parser.parse_args()
if args.threshold < 1:
parser.error("threshold must be positive")
counts = Counter()
with open(args.logfile, encoding="utf-8", errors="replace") as stream:
for line in stream:
fields = line.split(maxsplit=1)
if not fields:
continue
try:
address = str(ipaddress.ip_address(fields[0]))
except ValueError:
continue
counts[address] += 1
for address, count in counts.most_common():
if count >= args.threshold:
print(address, count)
python count_requests.py access-window.log --threshold 500
阈值的含义依赖窗口长度;一小时 500 次与一分钟 500 次完全不同。Counter 的内存也会随不同 IP 数增长,大规模日志可以交给日志平台或流式聚合处理。
真实 IP 与日志轮转
反向代理后的首字段可能是代理 IP。只有明确可信代理范围并正确配置 real_ip 后,才能按真实来源统计;不能无条件信任任意客户端传来的 X-Forwarded-For。
连续运行时不要只记住“上次读到第几行”:日志轮转、截断和进程重启都会使位置失效。应跟踪文件标识与偏移,或由日志采集器负责读取,把带时间戳的记录交给统计逻辑。
处置需要过期和恢复机制
可以先按路径、状态码、请求速率与业务结果判断异常,再选择限速、挑战或临时封禁。封禁应有理由、到期时间、允许列表和人工解除入口;不要让一个一次性脚本永久追加重复 DROP 规则。
Nginx 的 limit_req 更适合在请求进入时进行速率控制,但也需要选择正确的 key 和突发策略。上线前用正常用户与共享网络场景验证误伤率,再决定阈值。
参考:Nginx 日志模块、limit_req。