保护 Nginx 网站免受应用层(L7)攻击

发布于 21/08/2026 更新于 06/10/2026 52 次浏览
Sécurité VPS Linux Nginx DDoS

并非所有攻击都是一样的。流量型攻击旨在占满网络链路:这就像高速公路上的大堵车,由 By-Hoster 的防护系统在上游处理,您无需进行任何操作。

应用层攻击(即第 7 层攻击)则不同。流量并不大,但每个请求的处理成本都很高:搜索、登录页面、购物车。对网络而言,这些都是正常的访客。只有在您的 Web 服务器层面,才能看出这些流量的本质,因此必须在那里采取行动。

如何了解究竟是什么在冲击我的服务器?

在弄清楚发生了什么之前,不要做任何更改。随意设置的规则往往会挡住您的真实访客,却对攻击者毫无影响。

最活跃的 IP 地址:

awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20

请求最多的 URL:

awk -F'"' '{print $2}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20

最近一分钟的流量,用于判断攻击强度:

tail -n 100000 /var/log/nginx/access.log | awk '{print $4}' | uniq -c | tail -20

您要寻找的是一种模式:少数几个请求极其频繁的地址、某个被反复猛烈访问的 URL,或者到处都相同的用户代理。应当由这种模式来决定规则,而不是反过来。

另外,请检查真正达到饱和的是什么:

uptime
systemctl status php8.3-fpm --no-pager

很多时候,扛不住的并不是 Nginx,而是其背后的 PHP 或数据库。

如何在 Nginx 中按地址限制请求速率?

Nginx 可以按地址统计请求数,并对超出限制的请求进行限速。区域需要在 http 块(文件 /etc/nginx/nginx.conf)中声明:

http {
    limit_req_zone  $binary_remote_addr zone=req_par_ip:10m rate=10r/s;
    limit_conn_zone $binary_remote_addr zone=conn_par_ip:10m;
}

然后在网站的配置块中应用:

server {
    limit_req  zone=req_par_ip burst=20 nodelay;
    limit_conn conn_par_ip 20;
}

具体含义如下:

设置 效果
rate=10r/s 正常情况下,每个地址每秒 10 个请求
burst=20 允许在此速率之上出现 20 个请求的突发
nodelay 立即处理突发请求,而不是将其分摊开
limit_conn 20 每个地址最多 20 个并发连接

一个 10m 的区域大约可以记录 16 万个地址,绰绰有余。

不带 nodelay 的 burst 会将请求放入队列:网站会变慢而不是拒绝请求。使用 nodelay 后,正常访客加载一个页面及其二十个资源时不会受到任何减速,而反复请求的机器人则会收到 503 错误。

与其保护整个网站,不如对真正消耗资源的 URL 施加更严格的保护:

location = /wp-login.php {
    limit_req zone=req_par_ip burst=3 nodelay;
}

如何在应用 Nginx 配置之前进行测试?

使用 nginx -t 验证语法而不应用任何更改,然后执行热重载,而不是重启。

nginx -t

此命令会验证语法,而不会中断任何服务。它应返回 syntax is ok,然后返回 test is successful。之后才执行:

systemctl reload nginx

与 restart 不同,reload 会在不中断当前连接的情况下重新加载配置。在攻击进行时,这一区别至关重要。

监控被拒绝的请求:

grep "limiting requests" /var/log/nginx/error.log | tail -20

如果您看到自己或客户的地址出现在其中,说明限制设得太低了。请将其调高。

如何减少同一页面的重复生成?

抵御攻击的最佳方法,往往是不要在每次请求时都重新生成页面。缓存 60 秒的页面几乎不消耗任何资源,无论请求有多少。

http {
    proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=site:50m inactive=10m;
}
location / {
    proxy_cache site;
    proxy_cache_valid 200 60s;
    proxy_cache_use_stale error timeout updating;
}

proxy_cache_use_stale 是关键的一行:如果后端不再响应,Nginx 会继续提供最后一个已知版本,而不是返回错误。

在 WordPress、PrestaShop 或 Laravel 上,应用层缓存模块可以达到同样的效果,且产生副作用的风险更低。

如何使用 fail2ban 封禁屡次违规的地址?

限速只能减缓攻击;封禁才能阻止攻击。fail2ban 会读取日志,并对过于频繁触发限制的地址添加防火墙规则。

apt install fail2ban

在 /etc/fail2ban/jail.local 中:

[nginx-limit-req]
enabled  = true
filter   = nginx-limit-req
logpath  = /var/log/nginx/error.log
maxretry = 10
findtime = 60
bantime  = 3600
systemctl restart fail2ban
fail2ban-client status nginx-limit-req

用通俗的话来说,这条规则的意思是:在 60 秒内触发 10 次限速的地址将被封禁一小时。

面对应用层攻击时应避免哪些设置?

这些设置流传甚广,但弊大于利。

应避免 原因
根据用户代理屏蔽 curl 和 wget 攻击者只需一行代码就能更换用户代理。而您自己的监控、支付 Webhook 和备份脚本却会因此失效。
client_max_body_size 1k 所有图片上传、稍长的表单以及 CMS 更新都会失败。
对某个子网使用 allow,然后 deny all 您会对整个互联网关闭网站,也就是对您的访客关闭网站。请仅对管理后台 URL 使用这种方式。
出于本能封禁整个国家 误报很多,而且攻击者到处都能租到机器。
「为了性能」而禁用日志 您会失去了解所发生情况的唯一途径。

一条简单的规则:任何拦截范围超出所观察到模式的措施,都会以流失客户为代价。

什么时候应该向我们提交工单?

当问题超出了您在机器上能够解决的范围时:链路饱和、攻击持续时间长,或者涉及成千上万个地址。

在以下情况下请提交工单:

  • 尽管进行了上述设置,机器仍然无法访问;
  • 流量在到达 Nginx 之前就已占满网络链路;
  • 攻击持续时间长,且来自成千上万个不同的地址。

请在工单中附上第 1 步中的日志摘录以及相关的时间段。这能让我们在上游的网络层面采取行动,而那是您无法干预的地方。

这篇文章对您有帮助吗?