
网站突然变慢、CPU占用升高、PHP或数据库连接耗尽、Nginx出现大量499、502、503和504错误时,很多管理员会怀疑网站遭到CC攻击。
CC攻击通常通过大量看似正常的HTTP或HTTPS请求,持续访问动态页面、搜索接口、登录接口或其他高消耗资源,让Web服务器、应用进程和数据库逐渐失去响应。它与直接打满网络带宽的流量型DDoS不同,攻击流量可能不大,但每个请求都会触发真实业务计算。
处理CC攻击不能只依赖封禁几个IP。现代攻击可能使用代理网络、云主机、移动网络和大量真实客户端地址,简单封IP不仅效果有限,还可能误伤正常用户。
正确的处理顺序应该是:先确认故障是否由异常HTTP请求造成,保留日志和监控数据,再识别被攻击的域名与接口,随后通过CDN、WAF、Nginx限流、应用缓存和数据库保护逐层降低压力。
本文以Linux、Nginx和常见CDN场景为例,介绍一套可执行的CC攻击排查与防护方法。
一、什么是CC攻击
CC攻击通常属于应用层拒绝服务攻击。攻击者不一定发送超大数据包,而是模拟浏览器或客户端,反复请求网站中消耗资源较高的页面。
常见目标包括:
- WordPress登录页、搜索页和XML-RPC接口。
- 商品搜索、筛选和详情页面。
- 登录、注册、验证码和密码找回接口。
- 动态生成图片、PDF或报表的接口。
- 没有缓存的API。
- 会执行复杂数据库查询的URL。
- 文件不存在时仍由应用程序处理的请求。
- 绕过CDN直接访问源站IP的请求。
一次请求本身可能完全符合HTTP协议,但大量请求叠加后,会耗尽:
- Nginx或Apache连接。
- PHP-FPM、Gunicorn、Node.js等工作进程。
- 数据库连接池。
- CPU和内存。
- 磁盘I/O。
- 第三方API配额。
- 公网带宽和CDN回源流量。
二、先区分CC攻击和其他故障
网站访问异常不一定是攻击。程序发布错误、数据库慢查询、缓存失效、定时任务和正常活动流量,也可能造成类似现象。
1. 正常流量突增
特点通常是:
- 流量来源与推广、新闻或活动时间吻合。
- 用户会访问多个页面和静态资源。
- 转化、注册或订单等业务指标同步增加。
- 请求头、Cookie和访问路径相对完整。
2. 搜索引擎或普通爬虫
特点可能是:
- 请求具有一定抓取规律。
- User-Agent相对固定。
- 主要访问公开页面。
- 访问速度较快,但通常不会持续攻击单个高消耗接口。
User-Agent可以伪造,不能只凭名称认定请求来自搜索引擎。必要时应通过来源IP与官方验证方法确认。
3. CC攻击
常见特征包括:
- 大量请求集中访问少数动态URL。
- 多个IP使用相似的路径、参数和请求频率。
- 请求绕过正常页面流程,直接访问高成本接口。
- 静态资源请求很少,动态请求比例异常。
- Referer、Cookie、Accept-Language等请求头高度重复或明显缺失。
- 应用进程、数据库连接或CPU先耗尽,公网带宽不一定跑满。
4. 流量型DDoS
如果公网带宽被迅速打满、服务器甚至无法建立SSH连接,或者云平台显示大量异常网络流量,问题可能已经超出单台Nginx可以处理的范围。
此时应优先使用云平台DDoS防护、清洗服务、CDN或高防产品。请求到达Nginx之前,服务器网络就可能已经拥塞,本机防火墙和应用限流无法解决上游带宽瓶颈。
三、发生异常时先保留现场
不要在没有记录数据的情况下立即重启服务器。重启可能暂时恢复服务,却会丢失进程状态和短期流量特征。
先记录当前时间:
`bash date `
查看系统负载:
`bash uptime top `
查看内存:
`bash free -h `
查看连接状态:
`bash ss -s `
查看80和443端口的连接:
`bash sudo ss -ant ‘( sport = :80 or sport = :443 )’ | head `
查看Nginx进程和应用进程:
`bash ps -eo pid,ppid,user,%cpu,%mem,etime,comm,args –sort=-%cpu | head -n 30 `
同时保存:
- 云平台CPU、内存、带宽和连接数监控截图。
- CDN请求量、命中率、回源量和安全事件。
- Nginx访问日志与错误日志。
- PHP-FPM、Java、Node.js或其他应用日志。
- 数据库连接数、慢查询和锁等待。
四、从Nginx日志识别异常请求
先确认实际日志路径:
`bash sudo nginx -T | grep access_log `
以下命令假设日志位于 /var/log/nginx/access.log,实际使用时需要替换路径。
1. 统计访问最多的客户端IP
`bash awk ‘{print $1}’ /var/log/nginx/access.log \ | sort | uniq -c | sort -nr | head -n 30 `
如果网站经过CDN或反向代理,日志第一列可能是CDN节点地址,而不是真实访客IP。没有正确配置真实IP模块前,不要直接根据该字段封禁。
2. 统计访问最多的URL
常见日志格式中,请求路径可能位于第7列:
`bash awk ‘{print $7}’ /var/log/nginx/access.log \ | sort | uniq -c | sort -nr | head -n 30 `
如果日志格式经过自定义,应先查看样本:
`bash tail -n 20 /var/log/nginx/access.log `
再根据字段位置调整命令。
3. 按状态码统计
`bash awk ‘{print $9}’ /var/log/nginx/access.log \ | sort | uniq -c | sort -nr `
常见状态码含义:
200:请求成功,不代表请求一定正常。301或302:重定向。403:被访问控制或安全规则拒绝。404:资源不存在,可能存在扫描。429:请求过多,常用于限流响应。499:客户端在Nginx返回响应前断开。502、503、504:上游应用不可用、繁忙或超时。
4. 查看实时高频请求
`bash sudo tail -F /var/log/nginx/access.log `
线上流量较大时,不要长时间把全部日志输出到终端。可以结合 grep 观察目标接口:
`bash sudo tail -F /var/log/nginx/access.log | grep ‘/login’ `
5. 统计最近时间段
如果日志时间格式是常见的Nginx格式,可以先提取目标分钟的记录:
`bash grep ’06/Aug/2026:14:30′ /var/log/nginx/access.log \ | awk ‘{print $7}’ | sort | uniq -c | sort -nr | head `
修改为实际故障时间。不同语言环境和日志格式可能需要调整。
五、确认源站是否被绕过CDN
已经接入CDN的网站,仍可能因为源站IP暴露而被直接攻击。
检查方法包括:
- 对比CDN请求量和源站访问日志。
- 查看日志中的Host头是否异常。
- 检查源站是否接受任意IP直接访问。
- 检查历史DNS记录、邮件记录和其他子域名是否泄露源站IP。
- 查看云平台安全组是否允许全球地址访问80和443端口。
更稳妥的源站保护方式是:
- 只允许CDN回源地址访问源站80和443端口。
- 为CDN回源配置专用Host或验证请求头。
- 不在其他DNS记录中复用源站公网IP。
- 源站管理端口使用固定办公IP、VPN或堡垒机访问。
CDN回源地址可能更新,应使用服务商提供的官方地址列表或自动化同步方案,避免手工写入后长期不维护。
六、先通过CDN和WAF降低源站压力
CC攻击本质上是大量应用层请求,越早在源站之外拦截,服务器承受的压力越小。
可以采取:
- 为静态文件和可缓存页面设置合理缓存。
- 对登录、搜索、注册和API启用速率限制。
- 对明显自动化请求启用托管质询或浏览器验证。
- 使用WAF托管规则拦截常见攻击载荷。
- 对异常地域、ASN、User-Agent或路径组合设置临时规则。
- 为源站隐藏和访问控制设置独立规则。
不要在攻击期间直接开启过于严格的全站验证。这样可能影响搜索引擎、API客户端、支付回调和正常用户。
优先保护高成本接口,并为确认不需要浏览器访问的接口使用更明确的身份认证、签名或API网关规则。
七、使用Nginx限制请求速率
Nginx的 limit_req 模块可以按指定键限制请求处理速率。常见做法是按客户端IP建立共享内存区域。
在 http 配置块中定义:
`nginx limit_req_zone $binary_remote_addr zone=per_ip_rate:20m rate=5r/s; `
含义:
$binary_remote_addr:以二进制形式保存客户端地址。zone=per_ip_rate:20m:建立名为per_ip_rate的20MB共享区域。rate=5r/s:每个键平均每秒允许5个请求。
在需要保护的接口中使用:
`nginx location = /login { limit_req zone=per_ip_rate burst=10 nodelay; proxy_pass http://app_backend; } `
burst=10允许短时间内存在一定突发请求;nodelay表示突发额度内的请求不排队延迟处理。
建议返回明确的限流状态码:
`nginx limit_req_status 429; `
完整示例:
`nginx http { limit_req_zone $binary_remote_addr zone=per_ip_rate:20m rate=5r/s; limit_req_status 429;
server { listen 443 ssl; server_name example.com;
location = /login { limit_req zone=per_ip_rate burst=10 nodelay; proxy_pass http://app_backend; } } } `
不要把同一个严格阈值直接应用到全站。网页加载可能同时发起多个CSS、JavaScript、图片和接口请求,全站按IP限制过低会误伤共享网络、公司出口和移动运营商用户。
八、先用dry run观察误伤
支持相应指令的Nginx版本可以启用:
`nginx limit_req_dry_run on; `
Dry run模式不会真正限制请求,但会记录哪些请求本来会被限流。可以先运行一段时间,根据日志调整速率和突发额度,再正式启用。
建议流程:
- 只选择登录、搜索或高消耗接口。
- 开启dry run。
- 覆盖正常业务高峰。
- 检查正常用户、爬虫和第三方回调是否命中。
- 调整
rate和burst。 - 再关闭dry run正式执行。
九、限制并发连接
limit_conn模块可以限制每个键同时存在的连接或请求数量。
在 http 块定义:
`nginx limit_conn_zone $binary_remote_addr zone=per_ip_conn:20m; `
在目标位置启用:
`nginx location /api/ { limit_conn per_ip_conn 20; limit_conn_status 429; proxy_pass http://app_backend; } `
需要注意:
- 经过HTTP/2或HTTP/3时,每个并发请求可能被分别计数。
- NAT、公司网络和移动运营商出口可能有大量用户共享IP。
- WebSocket、SSE和长轮询连接持续时间较长,不能使用普通页面的阈值。
- 如果Nginx看到的是CDN节点IP,会把大量用户错误地归为同一个地址。
并发限制应根据接口类型设置,不建议全站使用统一数值。
十、经过CDN时正确获取真实IP
网站经过可信CDN或反向代理时,需要配置Nginx真实IP模块,否则限流和日志都可能只看到代理节点。
示例结构:
`nginx set_real_ip_from 192.0.2.0/24; real_ip_header X-Forwarded-For; real_ip_recursive on; `
这里的IP网段和请求头只是示例,必须替换为CDN官方公布的回源地址和指定真实IP头。
安全要点:
- 只信任CDN官方回源IP。
- 不要对所有来源无条件信任
X-Forwarded-For。 - 源站安全组尽量只允许CDN访问。
- CDN地址列表变化时及时同步。
如果任意客户端都能直接访问源站并自行填写真实IP请求头,攻击者就可能伪造地址绕过按IP限流。
十一、针对高风险接口设置单独规则
不同接口的正常访问频率差异很大,应该分开保护。
登录接口
可以同时使用:
- IP速率限制。
- 账号维度失败次数限制。
- 验证码或人机验证。
- 多因素认证。
- 登录失败延迟。
只按IP限制无法防止分布式尝试,也可能误伤共享出口用户。
搜索和筛选接口
建议:
- 限制每页数量和查询复杂度。
- 缓存热门查询。
- 限制通配符和深度分页。
- 为匿名用户设置更严格配额。
- 对高成本查询设置超时。
API
优先使用:
- API密钥或签名。
- 用户、应用和令牌维度配额。
- API网关限流。
- 请求体大小限制。
- 幂等控制。
- 明确的超时和熔断。
WordPress
重点检查:
/wp-login.php/xmlrpc.php/wp-admin/admin-ajax.php- 站内搜索
- 无缓存的动态页面
如果业务不需要XML-RPC,可以在确认插件和移动客户端不依赖它后限制访问。不要盲目禁用导致远程发布、Jetpack或其他功能异常。
十二、保护应用和数据库
Nginx只能限制进入应用层的请求数量,不能代替应用优化。
应用层
- 为动态页面和接口增加缓存。
- 设置合理的请求超时。
- 限制上传大小和请求体大小。
- 限制单次查询、导出和报表范围。
- 避免错误后立即无限重试。
- 为外部接口调用设置超时、重试上限和熔断。
- 将耗时任务放入队列异步处理。
PHP-FPM
检查进程池:
`bash sudo systemctl status php-fpm `
服务名称可能带版本号,例如 php8.3-fpm。
配置时重点关注:
pm.max_childrenpm.max_requestsrequest_terminate_timeout- 慢日志
pm.max_children过低会导致正常请求排队,过高则可能耗尽内存和数据库连接。应根据单个PHP进程实际内存与服务器容量计算。
数据库
检查:
- 活跃连接和连接池。
- 慢查询。
- 锁等待。
- 缺失索引。
- 被重复调用的高成本SQL。
- 临时表、排序和全表扫描。
攻击常常只是放大了原本存在的性能问题。一个正常访问也很慢的接口,在高并发下更容易拖垮数据库。
十三、是否应该直接封禁IP
临时封禁少量明确异常的IP可以快速降低压力,但不应作为长期核心方案。
适合临时封禁的情况:
- 单个或少量IP持续高频攻击。
- 来源明确且不存在正常用户。
- 攻击正在影响服务,需要立即缓解。
不适合只靠封IP的情况:
- 攻击地址数量很多并持续变化。
- 大量用户共享NAT出口。
- 网站经过CDN但真实IP配置不正确。
- 攻击使用合法云服务或移动网络地址。
- 攻击绕过CDN直接打源站。
封禁前应保留日志,设置到期时间,并定期清理临时规则,避免防火墙长期积累无效条目。
十四、Nginx配置上线前必须测试
修改配置后执行:
`bash sudo nginx -t `
测试通过后重新加载:
`bash sudo systemctl reload nginx `
不要直接重启Nginx来验证配置。重新加载通常可以保留现有连接,并降低配置错误带来的影响。
上线后观察:
- 429状态码数量。
- 正常用户错误率。
- 登录和支付回调。
- 搜索引擎抓取。
- CDN回源状态。
- 上游响应时间。
- CPU、内存和数据库连接。
十五、攻击期间的推荐处理顺序
- 保存系统监控、CDN数据和访问日志。
- 判断是应用层CC还是流量型DDoS。
- 找出被集中访问的域名、URL和接口。
- 确认日志记录的是真实客户端IP。
- 启用CDN或WAF的临时防护规则。
- 限制源站只接受可信CDN回源。
- 对高成本接口启用Nginx请求速率和并发限制。
- 使用dry run或小范围规则验证误伤。
- 为动态页面、API和数据库增加缓存与超时。
- 临时关闭非必要的高消耗功能。
- 攻击缓解后复盘源站暴露、慢接口和监控缺口。
- 将临时规则整理为可维护的长期防护策略。
十六、建立长期监控
建议持续监控:
- 每秒请求数。
- 各状态码数量。
- 访问最多的域名和URL。
- CDN命中率与回源请求量。
- Nginx活动连接。
- 上游响应时间。
- 应用工作进程数量。
- 数据库连接与慢查询。
- CPU、内存、磁盘I/O和公网带宽。
- 429、502、503和504错误。
仅设置“CPU超过90%”告警通常不够。CC攻击可能先耗尽数据库连接或PHP工作进程,而CPU尚未完全跑满。
更有效的告警方式是同时关注请求速率、响应时间、错误率和资源使用情况。
总结
CC攻击通过大量高成本HTTP请求消耗Web服务器、应用进程和数据库资源。它不一定打满公网带宽,也不一定来自少量固定IP,因此简单重启服务器或批量封禁IP通常只能短暂缓解问题。
排查时应从访问日志、系统负载、连接状态和CDN回源数据入手,确认攻击目标与真实客户端来源。防护时应优先在CDN、WAF和源站访问控制层减少无效流量,再使用Nginx的请求速率限制和并发限制保护关键接口。
长期解决方案还包括缓存、接口认证、数据库优化、超时控制、源站隐藏和多维度监控。只有把边缘防护、Web服务器限制和应用性能治理结合起来,网站才不容易在下一次异常流量出现时再次失去响应。




