前言
上一篇《网站CPU突然跑满,很可能是CC攻击》,写了CC攻击的排查方式,今天就来具体写一写怎么在 VPS 上配置这层防护,还有一些比较容易犯的错,比如限流配太严把自己封了、缓存规则写错导致后台登录不上、源站白名单设错把 CDN 节点全挡了等等,给朋友们一个参考。
在 VPS 上能做哪些防护?
上篇把长期防护分成了五层:缓存层、限流层、WAF 层、CDN 层、源站保护。其中 CDN 层主要靠 CDN 服务商,VPS 上能做的是另外四层。
我的 VPS 环境是 Ubuntu 22.04 + Nginx + PHP-FPM,面板用的 1Panel。以下配置都基于这个环境,其他环境思路类似,一些细节需要自行调整。

缓存层:让能缓存的都缓存
CC 攻击打的是动态处理能力,那最直接的对策就是把动态请求变成静态请求。
Nginx 的 FastCGI 缓存 可以把 PHP 渲染后的页面缓存起来,后续相同请求直接返回缓存,不再经过 PHP 和数据库。配置大致是这样:
# 在 http 块中定义缓存路径
fastcgi_cache_path /var/cache/nginx/fastcgi levels=1:2 keys_zone=fastcgi_cache:100m inactive=60m max_size=1g;
# 在 server 块中启用缓存
set $skip_cache 0;
if ($request_method = POST) { set $skip_cache 1; }
if ($query_string != "") { set $skip_cache 1; }
if ($request_uri ~* "/wp-admin/|/login|/comment") { set $skip_cache 1; }
fastcgi_cache fastcgi_cache;
fastcgi_cache_valid 200 301 302 10m;
fastcgi_cache_bypass $skip_cache;
fastcgi_no_cache $skip_cache;
add_header X-FastCGI-Cache $upstream_cache_status;这段配置的逻辑是:POST 请求、带查询参数的请求、后台和登录页面不走缓存,其他页面缓存 10 分钟。X-FastCGI-Cache 头可以帮你确认缓存是否命中。
我配置的时候,一开始忘了排除登录页,结果登录时一直显示缓存页面,输入验证码怎么都不对。(╯°□°)╯︵ ┻━┻ 后来把 /login、/wp-admin/、/comment 都加进排除规则才正常。
缓存命中率上去之后,CPU 立刻降了一截。静态资源图片、CSS、JS 建议直接放 对象存储 或让 CDN 缓存,不经过 VPS 更好。
限流层:给动态接口加 Rate Limiting
缓存解决的是“可缓存的请求”,但搜索、登录、评论提交这些接口必须实时处理,没法缓存。对这类接口,用 Nginx 的 limit_req 模块做限流。
先在 http 块定义限流区:
limit_req_zone $binary_remote_addr zone=search_limit:10m rate=5r/s;
limit_req_zone $binary_remote_addr zone=login_limit:10m rate=2r/s;
limit_req_zone $binary_remote_addr zone=comment_limit:10m rate=1r/s;然后在对应的 location 中应用:
location = /search {
limit_req zone=search_limit burst=10 nodelay;
# ... 其他配置
}
location = /login {
limit_req zone=login_limit burst=5 nodelay;
# ... 其他配置
}rate=5r/s 表示每个 IP 每秒允许 5 个请求,burst=10 允许突发 10 个请求排队,nodelay 表示突发请求立即处理而不是延迟。超过阈值的请求会返回 503 或 429。
我一开始把登录接口的 rate 设成 1r/s,结果自己登录时多点了几次就触发了限流,把自己锁在外面了。(´-ω-`) 后来调整到 2r/s 加 burst=5,这个数值比较合适,正常用户完全感觉不到,攻击者的高频请求也会被挡住。
WAF 层:用 Nginx 规则拦截异常请求
完整的 WAF 可以上 ModSecurity,但对个人博客来说,Nginx 自带的一些规则就够用。
拦截异常 User-Agent:
if ($http_user_agent ~* (nikto|sqlmap|nmap|masscan|python-requests|curl|wget)) {
return 403;
}拦截高频 POST 请求:结合 limit_req 对 POST 请求单独限流。
拦截异常参数:
if ($args ~* "(eval|base64|concat|union|select|insert|drop)") {
return 403;
}这些规则不能替代专业 WAF,但能挡掉大部分自动化扫描和低级别攻击。我配完这些规则后,日志里的异常请求明显少了。
源站保护:只允许 CDN 回源
如果你的网站前面挂了 CDN,那源站必须只允许 CDN 节点访问,否则攻击者可以绕过 CDN 直接打源站 IP。
首先获取 CDN 服务商的回源 IP 段,然后在 VPS 防火墙上设置白名单。用 iptables 的话:
# 允许 CDN 回源 IP 段访问 80 和 443
iptables -A INPUT -p tcp -s 1.2.3.0/24 --dport 80 -j ACCEPT
iptables -A INPUT -p tcp -s 1.2.3.0/24 --dport 443 -j ACCEPT
# 拒绝其他所有对 80 和 443 的访问
iptables -A INPUT -p tcp --dport 80 -j DROP
iptables -A INPUT -p tcp --dport 443 -j DROP用 ufw 的话,可以先允许 CDN IP 段,再拒绝其他:
ufw allow from 1.2.3.0/24 to any port 80
ufw allow from 1.2.3.0/24 to any port 443
ufw deny 80
ufw deny 443也可以用 Nginx 的 allow 和 deny 指令:
location / {
allow 1.2.3.0/24;
deny all;
# ... 其他配置
}我刚开始只加了 CDN 回源 IP,忘了加自己的办公 IP,结果自己访问网站也被挡了。(;´д`)ゞ 后来把自己的 IP 也加进白名单才正常。另外,CDN 的回源 IP 段可能会更新,要定期检查。
防火墙层:用 fail2ban 自动封禁
手动封 IP 效率太低,fail2ban 可以自动分析 Nginx 日志,发现高频异常请求就自动封禁 IP。
安装 fail2ban 后,配置 Nginx 的 jail:
[nginx-limit-req]
enabled = true
filter = nginx-limit-req
action = iptables-multiport[name=nginx-limit-req, port="http,https"]
logpath = /var/log/nginx/error.log
maxretry = 5
bantime = 3600这表示 5 次触发限流就封禁 1 小时。fail2ban 配合 Nginx 限流,能自动处理大部分攻击 IP。
测试
配置完成后一定要测试:
限流测试:用
ab或wrk模拟高频请求,确认限流生效,同时确认正常访问不受影响。缓存测试:检查
X-FastCGI-Cache头,确认缓存命中,同时确认登录、评论等动态页面不被缓存。源站保护测试:从非白名单 IP 访问源站,确认被拒绝;从白名单 IP 访问,确认正常。
fail2ban 测试:模拟高频请求,确认 IP 被封禁,并确认解封时间正确。
总结
在 VPS 上给网站加防护,核心思路是把请求处理尽量前移:
缓存层:Nginx FastCGI 缓存,让动态页面变静态。
限流层:limit_req 对搜索、登录、评论接口限流。
WAF 层:Nginx 规则拦截异常 User-Agent 和参数。
源站保护:防火墙白名单,只允许 CDN 回源。
防火墙层:fail2ban 自动封禁高频 IP。
这几层配合起来,源站压力能降低不少。当然,自己配置需要花时间,也需要不断调整。如果不想折腾,也可以直接用云服务商的带 WAF 和 CDN 的托管方案,把网站防护交给云服务商。