网站突然变慢,先别急着重启 VPS。
如果你看到 CPU 飙高、带宽跑满、PHP-FPM 或后端应用一片超时,Nginx access.log 里同一个路径被疯狂刷新,这更像是 CC 攻击 / 恶意爬虫 / 接口滥用,不是普通的“访问量变大”。
这类问题最麻烦的地方在于:请求看起来都是正常 HTTP 请求,不像传统 DDoS 那样只是大流量冲击。它可能刷首页、刷搜索页、刷登录页、刷 API,也可能用一堆分散 IP 慢慢把你的数据库和 PHP-FPM 打满。
本文按真实处理顺序来:先确认攻击,再让 Cloudflare 在边缘挡一层,最后用 Nginx 在源站做兜底限速。
最直观的信号通常有这几个:
- VPS 的 CPU 或 load average 突然升高;
access.log请求量异常,比平时多出几倍到几十倍;- 某个路径被反复请求,例如
/wp-login.php、/xmlrpc.php、/search、/api/*; - Nginx 还能响应,但后端开始 502、504;
- Cloudflare Analytics 或服务商流量图出现异常尖峰;
- 正常用户访问慢,但静态资源可能还正常。
先看最近访问量最高的 IP:
sudo awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20
再看被刷得最多的路径:
sudo awk '{print $7}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20
如果你的网站在 Cloudflare 后面,Nginx 日志里可能全是 Cloudflare 节点 IP,这时要先配置真实访客 IP,否则你会误判。
Nginx 里常见做法是使用 CF-Connecting-IP:
set_real_ip_from 173.245.48.0/20;
set_real_ip_from 103.21.244.0/22;
set_real_ip_from 103.22.200.0/22;
set_real_ip_from 103.31.4.0/22;
set_real_ip_from 141.101.64.0/18;
set_real_ip_from 108.162.192.0/18;
set_real_ip_from 190.93.240.0/20;
set_real_ip_from 188.114.96.0/20;
set_real_ip_from 197.234.240.0/22;
set_real_ip_from 198.41.128.0/17;
set_real_ip_from 162.158.0.0/15;
set_real_ip_from 104.16.0.0/13;
set_real_ip_from 104.24.0.0/14;
set_real_ip_from 172.64.0.0/13;
set_real_ip_from 131.0.72.0/22;
real_ip_header CF-Connecting-IP;
Cloudflare IP 段会更新,生产环境建议定期核对官方列表。配置后先执行:
sudo nginx -t
sudo systemctl reload nginx
如果你还不熟悉日志定位,可以先看这篇:
如果攻击流量已经经过 Cloudflare,优先在 Cloudflare 处理。原因很简单:在边缘挡掉请求,才不会消耗你的 VPS CPU、内存、带宽和数据库连接。
临时应急可以先做三件事:
- 打开 Security / WAF 里的托管规则;
- 对异常路径加 Custom Rule;
- 对登录、搜索、API 这类重路径加 Rate Limiting Rule。
例如攻击集中在 /wp-login.php,可以先加一条更严格的规则:
- 匹配路径:
/wp-login.php - 统计维度:IP
- 阈值:1 分钟 5-10 次
- 动作:Managed Challenge 或 Block
- 封禁时间:10 分钟到 1 小时
如果是接口被刷,比如 /api/search,阈值可以比登录页宽一些:
- 1 分钟 60-120 次;
- 超过后返回 429 或 Managed Challenge;
- 对已登录用户、内部监控或可信 IP 做例外。
不要一上来就全站 Block。很多小站误伤就是这么来的:把搜索引擎、支付回调、监控探针、正常移动端用户一起挡掉,攻击是缓解了,业务也没了。
Cloudflare Rate Limiting 官方也提醒:限速不是精确放行固定请求数,计数和拦截之间可能有几秒延迟。所以源站仍然要有兜底配置。
Nginx 的 limit_req 用的是类似“漏桶”的方式:请求可以短时间突发,但长期速率不能超过你设定的阈值。
先在 http 块里定义限速区域,通常放在 /etc/nginx/nginx.conf 或 /etc/nginx/conf.d/limits.conf:
limit_req_zone $binary_remote_addr zone=site_limit:10m rate=10r/s;
limit_req_zone $binary_remote_addr zone=login_limit:10m rate=1r/s;
limit_conn_zone $binary_remote_addr zone=addr_limit:10m;
含义大致是:
- 普通页面:单 IP 每秒 10 个请求;
- 登录/敏感接口:单 IP 每秒 1 个请求;
10m的共享内存足够跟踪大量 IP;$binary_remote_addr比普通 IP 字符串更省内存。
然后在站点配置里应用:
server {
server_name example.com;
limit_req zone=site_limit burst=30 nodelay;
limit_conn addr_limit 20;
limit_req_status 429;
limit_conn_status 429;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location = /wp-login.php {
limit_req zone=login_limit burst=5 nodelay;
limit_req_status 429;
include fastcgi_params;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
location = /xmlrpc.php {
deny all;
}
}
这里有几个细节很重要:
burst=30是给正常浏览器加载页面时的短暂突发空间;nodelay表示突发请求不排队等待,超出后直接按规则处理;limit_req_status 429比默认 503 更清楚,表示请求太多;- WordPress 的
xmlrpc.php如果不用,建议直接关掉; - API、登录、搜索页应该分开限速,不要全站一个阈值打天下。
修改后一定先测试:
sudo nginx -t
sudo systemctl reload nginx
如果你最近刚改过 Nginx 配置,遇到 reload 失败,可以先看:
限速不是越严越好。
我见过一个小站把全站限制成 1r/s,结果首页有十几个 CSS、JS、图片资源,用户一打开页面就被 429。攻击没完全挡住,正常用户先被挡在外面。
上线后先看 Nginx error log:
sudo tail -f /var/log/nginx/error.log | grep "limiting requests"
如果看到类似:
limiting requests, excess: 12.450 by zone "site_limit", client: 203.0.113.10
说明这个 IP 正在触发限速。
再按状态码统计一下:
sudo awk '{print $9}' /var/log/nginx/access.log | sort | uniq -c | sort -rn
如果 429 突然很多,继续看路径:
sudo awk '$9 == 429 {print $7}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20
如果 429 都集中在 /wp-login.php、/xmlrpc.php、/api/search,这通常是好事;如果首页、静态资源、健康检查也大量 429,就说明规则太粗。
很多人接了 Cloudflare 之后就放心了,但源站 IP 如果已经泄露,攻击者可以直接访问你的 VPS IP,绕过 Cloudflare。
这种情况的典型信号是:
- Cloudflare 上看不到太多请求;
- VPS 带宽和 Nginx 日志却很高;
- Nginx 里出现大量非 Cloudflare 来源 IP;
- 直接访问
http://你的源站IP能打开网站。
至少要做两件事。
第一,Nginx 默认站点不要直接服务真实网站:
server {
listen 80 default_server;
server_name _;
return 444;
}
第二,防火墙只允许 Cloudflare IP 访问 80/443。这一步要谨慎,先确认你能通过 SSH 登录,且 Cloudflare IP 段完整,否则可能把自己网站断掉。
如果源站 IP 已经被长期盯上,最干净的办法通常是:
- 新开一台 VPS 或更换公网 IP;
- 只把新源站 IP 填到 Cloudflare DNS;
- 防火墙限制 80/443 只接受 Cloudflare;
- 不在邮件头、Git 仓库、历史 DNS、监控页面里泄露新 IP。
如果攻击导致 Cloudflare 521/522,可以参考:
如果攻击来自少量固定 IP,封 IP 有用:
sudo ufw deny from 203.0.113.10
或者临时用 iptables:
sudo iptables -I INPUT -s 203.0.113.10 -j DROP
但如果攻击来自成百上千个 IP,手动封 IP 基本没意义。你会把规则表搞得越来越乱,CPU 还要继续处理大量连接。
这时更适合:
- Cloudflare WAF / Rate Limiting;
- Nginx
limit_req和limit_conn; - Fail2Ban 自动封持续触发限速的 IP;
- 服务商高防或更上游的 DDoS 防护。
如果你已经怀疑机器被入侵,而不是单纯被请求打满,要先按入侵处置流程走:
如果网站正在被刷,我会按这个顺序处理:
- 先看 Nginx access.log,确认高频 IP 和高频路径;
- 如果走 Cloudflare,先开 WAF / Managed Challenge / Rate Limiting;
- 对登录、搜索、API、
xmlrpc.php做重点保护; - Nginx 加
limit_req、limit_conn、429 状态码; - 检查真实 IP 配置,避免 Nginx 只看到 Cloudflare 节点 IP;
- 防火墙限制源站只接受 Cloudflare 访问;
- 观察 429、403、502、504 的变化,避免误伤正常用户;
- 攻击结束后复盘:源站 IP 是否泄露、哪些路径最贵、是否需要缓存或架构拆分。
别把限速当成万能药。真正大规模的流量型 DDoS,Nginx 来不及处理就会被打满,必须靠 Cloudflare、服务商高防或上游清洗。Nginx 限速更适合挡中小规模 CC、恶意爬虫、接口滥用、登录爆破这种 HTTP 层请求。
有可能。尤其是你把全站限速设得太低,或者没有给 Cloudflare Verified Bots、Googlebot、Bingbot 留足空间。更稳的做法是优先限制登录、搜索、接口、动态页,不要直接对静态资源和正常页面下死手。
普通小站大多数时候够用。免费版能提供 CDN、基础 DDoS 防护、WAF 规则和部分 Bot 防护能力。但更精细的 Rate Limiting、Bot Management、复杂统计维度通常需要更高套餐。
少量固定 IP 可以。分布式攻击不建议手动封,应该用 Cloudflare、Nginx 限速、Fail2Ban 和服务商防护组合处理。
限速更适合返回 429,语义是 Too Many Requests。明确恶意规则、黑名单、空 User-Agent、攻击工具特征可以返回 403。
VPS 网站被 CC 攻击时,最怕两个极端:一个是只会重启服务,另一个是一上来就全站封死。
更稳的做法是分层处理:Cloudflare 在边缘挡大头,Nginx 在源站用 limit_req 和 limit_conn 做兜底,防火墙避免源站 IP 被绕过,日志持续观察误伤和攻击路径。
如果你只记一条:先用日志确认攻击路径,再限速最贵的路径,不要盲目全站限速。
