小型服务器站点做CC防御,最尴尬的地方在于资源预算极其有限。1核1G或者2核2G的机器,本身跑业务就已经紧巴巴,再套一层复杂的安全策略,CPU和内存占用一上来,网站打开速度反而更慢。轻量化CC防御的核心不是堆规则,而是用最小的性能代价识别并丢弃恶意流量,让真实用户的请求能顺利到达后端。

先明确一个基本判断:小型站点遭遇的CC攻击,绝大多数不是超大流量冲击,而是中低速率、短时高频的请求,攻击者用几百到几千个代理IP反复请求动态页面或搜索接口,目的就是耗尽你的PHP进程、数据库连接数或者带宽。所以防御思路要围绕“在边缘快速识别、在入口快速丢弃”展开,而不是等请求进入应用层再做复杂分析。

第一步:把CC防御前置到Web服务器层

Nginx是小型服务器上最常用的Web服务,轻量化CC防御的第一道闸门就应该设在这里。很多人一上来就装Fail2ban或者ModSecurity,其实对资源敏感的小机器来说,Nginx自带的限速模块已经能解决大部分问题。核心配置只有几行,但参数要调得精准。

针对全站请求做一个基础频率限制,放在http块里:

limit_req_zone $binary_remote_addr zone=cc_base:10m rate=20r/s;
limit_req_status 429;

这里的rate=20r/s看起来不低,但它是针对单个IP的。正常用户浏览网页,每秒请求数极少超过5个,20r/s的阈值已经能拦截掉大部分脚本化攻击。10m的共享内存大约能存16万个IP状态,对小型站点完全够用。关键点在于使用$binary_remote_addr而不是$remote_addr,前者是二进制格式,内存占用只有后者的四分之一,这就是轻量化的细节。

然后在server或location块里应用:

location / {
    limit_req zone=cc_base burst=30 nodelay;
    try_files $uri $uri/ /index.php?$query_string;
}

burst=30 nodelay的意思是允许短时突发30个请求,但超过rate的部分立即处理而不是排队等待。这个组合在防御CC的同时,不会误伤浏览器同时加载多个静态资源的正常行为。如果你的站点以动态内容为主,可以把rate降到10r/s,burst调到15。

第二步:对动态入口单独设置更严格的限制

全站限速只能挡住最粗暴的攻击,稍微聪明一点的攻击者会降低请求频率,专门打搜索、登录、评论提交这类消耗资源的动态接口。这些接口的请求量本来就不应该高,所以可以单独设置更低的阈值。

location /search {
    limit_req zone=cc_search burst=5 nodelay;
    limit_req_status 429;
    include fastcgi_params;
    fastcgi_pass unix:/run/php/php8.1-fpm.sock;
}

location /wp-login.php {
    limit_req zone=cc_login burst=3 nodelay;
    limit_req_status 429;
    include fastcgi_params;
    fastcgi_pass unix:/run/php/php8.1-fpm.sock;
}

对应的limit_req_zone定义:

limit_req_zone $binary_remote_addr zone=cc_search:5m rate=2r/s;
limit_req_zone $binary_remote_addr zone=cc_login:5m rate=1r/s;

搜索接口每秒2次、登录接口每秒1次,对正常用户来说已经非常宽松,但对CC攻击工具来说,这个频率根本打不出伤害。攻击者要绕过这个限制,就必须投入更多代理IP,成本直线上升,而你的服务器资源几乎不受影响。

第三步:用Nginx的map和if做轻量级UA过滤

CC攻击的请求头往往带有明显的工具特征,比如空User-Agent、过短的UA、或者包含特定扫描器名称。在Nginx里做UA过滤,不需要引入第三方模块,用map配合if就能实现,而且性能开销极低。

map $http_user_agent $is_bad_ua {
    default 0;
    "~*sqlmap" 1;
    "~*nikto" 1;
    "~*fuzz" 1;
    "~*curl" 1;
    "~*python-requests" 1;
    "~*go-http-client" 1;
    "" 1;
}

server {
    if ($is_bad_ua = 1) {
        return 444;
    }
}

return 444是Nginx特有的非标准状态码,意思是直接关闭连接,不返回任何内容。这比返回403更省资源,因为连响应体都不用生成。空UA的拦截要谨慎,有些合法的监控服务或API调用可能不带UA,如果你的站点没有这类需求,可以加上;如果有,就把空UA那行去掉。

第四步:连接数限制比请求数限制更省资源

对于小型服务器,限制单个IP的并发连接数往往比限制请求速率更有效。CC攻击的一个典型特征是同一IP短时间内发起大量并发连接,把服务器的连接队列占满。Nginx的limit_conn模块就是干这个的。

limit_conn_zone $binary_remote_addr zone=cc_conn:10m;

server {
    limit_conn cc_conn 10;
    limit_conn_status 503;
}

单个IP最多同时保持10个连接,对正常浏览完全足够。攻击者如果想用并发连接打垮你,单IP超过10个连接就会被拒绝。这个规则在内存和CPU上的开销几乎可以忽略不计,但拦截效果非常直接。如果你的站点有大量静态资源,可以把连接数调到15或20,避免误伤。

第五步:利用内核参数优化TCP层防护

CC攻击很多时候是短连接攻击,大量的SYN包和快速建立又断开的连接会消耗内核资源。调整Linux内核参数,可以在不增加任何软件开销的情况下提升抗CC能力。

net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_max_syn_backlog = 2048
net.ipv4.tcp_synack_retries = 2
net.ipv4.tcp_fin_timeout = 30
net.ipv4.tcp_keepalive_time = 600
net.ipv4.tcp_max_tw_buckets = 5000

tcp_syncookies开启后,SYN队列满时不会直接丢弃新连接,而是用cookie机制验证客户端真实性,这是防御SYN Flood的经典手段。tcp_synack_retries设为2,减少半开连接的重试次数,让攻击者伪造的SYN包更快被清理。tcp_fin_timeout设为30秒,加速TIME_WAIT状态连接的回收,避免连接表被占满。这些参数对小型服务器尤其重要,因为默认值通常偏保守,在高并发攻击下容易把内存和文件描述符耗尽。

第六步:PHP-FPM层面的轻量防护

如果CC攻击已经穿透了Nginx层,打到了PHP-FPM,那么最后一道防线就是限制PHP进程的请求处理能力。很多小型站点被打挂,不是因为流量有多大,而是PHP-FPM的进程数被占满,导致正常请求无法处理。

在php-fpm.conf或www.conf里调整:

pm = dynamic
pm.max_children = 10
pm.start_servers = 3
pm.min_spare_servers = 2
pm.max_spare_servers = 5
pm.max_requests = 500

pm.max_children设为10,意味着同时最多只有10个PHP进程在处理请求。这个数字要根据你的内存来定,1G内存的机器,每个PHP进程大约占用30-50MB,10个进程就是300-500MB,加上Nginx和MySQL,基本就是极限了。pm.max_requests设为500,让每个进程处理500个请求后自动重启,防止内存泄漏累积。这个配置的价值在于,即使CC攻击打进来了,PHP进程也不会无限制增长导致OOM,服务器不会彻底死机,最多是响应变慢。

第七步:用日志分析辅助动态调整策略

轻量化CC防御不是配置一次就一劳永逸,攻击者的手法在变,你的策略也要跟着调整。但小型服务器不适合跑复杂的日志分析工具,用简单的shell命令就够了。

查看当前请求最频繁的IP:

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

查看请求最频繁的URL:

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

如果发现某个IP的请求量远超正常水平,可以直接用iptables封禁:

iptables -A INPUT -s 攻击IP -j DROP

如果发现某个URL被集中攻击,可以针对这个URL单独设置更严格的limit_req,或者直接返回444。这种动态调整的方式,比安装复杂的WAF更灵活,也更适合资源紧张的小服务器。

轻量化CC防御的配置优先级总结

对于小型服务器站点,CC防御的配置顺序应该是:先做Nginx层的请求频率限制和连接数限制,这是性价比最高的两道防线;然后针对动态入口做单独限制,保护最容易被打垮的环节;接着做UA过滤和内核参数优化,进一步压缩攻击者的操作空间;最后才是PHP-FPM层面的进程限制,确保服务器不会因为攻击而完全宕机。这个顺序的逻辑是:越靠前的防线,资源消耗越低,拦截效率越高,越适合小型服务器的实际情况。

很多人一上来就装Cloudflare或者接入高防CDN,对小型站点来说,这当然有效,但会增加延迟和成本。如果你的服务器在国内,CDN接入还需要备案和域名解析调整,流程繁琐。轻量化CC防御的意义就在于,用服务器自带的工具和少量配置,解决大部分中低强度的CC攻击,把宝贵的预算留给更重要的业务环节。当攻击强度超过服务器本身的承受能力时,再考虑接入外部防护也不迟。