网站被CC攻击打挂怎么应急?降级与错误页实战

被 CC 攻击打挂和被打 DDoS 不一样:流量可能只有几十 Mbps,服务器 CPU 却飙升到 100%,页面越开越慢直到 502。这时候最快的恢复手段不是”封 IP”,而是主动降级——把动态功能降成静态、把超限请求变成有信息的错误页。本文用 8 个问答讲清 CC 应急降级的完整做法,配置可直接复制使用。

Q:网站被 CC 打挂了,第一件事做什么?

先确认攻击入口,再动手。打开访问日志统计请求量最高的 URL:

awk '{print $7}' access.log | sort | uniq -c | sort -rn | head -20

如果日志里某个搜索页、详情页或接口的请求量远超其他 URL,来源 IP 却五花八门,那基本就是 CC。判断依据是三个特征:流量不大但源站 CPU 高请求集中在动态页面IP 分散且请求参数高度相似

确认之后立刻做两件事:Nginx 层加限速止血把被打入口降级为缓存或静态兜底。不要一开始就忙着封 IP——CC 走代理池,IP 一天能换几万个,手动封禁永远追不上。

Q:什么是服务降级?为什么 CC 应急一定要用降级?

降级的本质是在资源紧张时主动砍掉非核心功能,把有限资源留给核心请求。CC 攻击消耗的是动态计算资源:数据库查询、模板渲染、搜索匹配。降级就是针对这一点,让被攻击的请求”不算”——直接由 Nginx 返回缓存内容或静态文件,不落到后端程序。

相比封 IP,降级有三个明显优势:生效快(改配置 reload 即可,秒级);不依赖识别攻击者(不需要知道哪个 IP 是恶意的,不会误伤正常用户);可控(核心页面继续可用,只牺牲非核心功能)。CC 场景下,降级是性价比最高的应急手段。

Q:Nginx 怎么快速做限速降级?给一套可直接用的配置

第一步是全局限速止血,在 http 块定义限速区:

http {
    # 单 IP 每秒 10 个请求,单 IP 并发连接上限
    limit_req_zone  $binary_remote_addr zone=cc:10m rate=10r/s;
    limit_conn_zone $binary_remote_addr zone=cc_conn:10m;
    # 超限统一返回 503,便于走自定义页
    limit_req_status  503;
    limit_conn_status 503;

    server {
        # 动态入口限速 + 并发限制
        location ~ \.(php|jsp|asp)$ {
            limit_req  zone=cc burst=20 nodelay;
            limit_conn cc_conn 20;
            proxy_pass http://backend;
        }
        # 被打到的高消耗入口直接降级到静态页
        location = /search {
            return 302 /search-static.html;
        }
    }
}

参数说明:rate=10r/s 对正常访客完全够用(一个人不可能每秒点 10 次),CC 的密集请求会被大量丢弃;burst=20 nodelay 允许短时突发 20 个请求直接处理,避免用户刷新时被误限;limit_conn 20 限制单 IP 并发,主要防 Slowloris 类慢连接。整个文件改完执行 nginx -t && nginx -s reload 即生效。

Q:怎么给限流请求写一个友好的错误页?

不要直接甩 502/白屏,做一个纯静态的错误提示页:

limit_req_status  503;
limit_conn_status 503;

error_page 503 /cc-busy.html;

location = /cc-busy.html {
    root /usr/share/nginx/html;
    internal;
    add_header Retry-After 10 always;
}

要点有三个:错误页必须是纯静态 HTML,不含任何动态请求,否则降级页自己也会拖垮后端;必须返回真实的 503 状态码,并在响应头加 Retry-After,告诉浏览器和搜索引擎多久后重试;页面文案要清楚,写明”当前访问人数较多,请稍后刷新”,不要只放一行报错。

这里有个容易被忽略的 SEO 细节:搜索引擎把 503 + Retry-After 视为”临时不可用”,抓取失败但不会降低排名;而如果为了”看起来正常”返回 200 却放着错误内容,会被判定为软 404 或内容异常,反而伤权重。

Q:登录、注册、下单这类动态接口怎么降级?

这些接口消耗最大(查库 + 写库),要分三层处理。

第一层,临时关闭非核心接口。搜索、评论、列表筛选这类功能对业务影响小但对资源消耗大,CC 期间直接关掉:

location = /api/search   { return 503; }
location = /comments     { return 503; }

第二层,读接口走缓存。如果有 Redis,把高频读切到缓存并设置短 TTL;没有缓存的可以用 Nginx 做一层临时缓存,让相同请求不再每次都打后端:

proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=api:10m inactive=1h max_size=512m;

location /api/ {
    limit_req zone=cc burst=20 nodelay;
    proxy_cache api;
    proxy_cache_valid 200 10s;
    # 后端异常时用过期缓存兜底,避免直接 502
    proxy_cache_use_stale error timeout updating http_500 http_502 http_503;
    proxy_pass http://backend;
}

第三层,写接口加验证。登录、注册、短信验证码接口是 CC 与撞库攻击的共同目标,必须加人机验证和严格频率限制,例如单 IP 每分钟 1 次。这一层不能靠降级,只能靠验证和限频。

Q:某个 URL 到底该降级还是该直接封?

用三个维度判断,避免”一刀切”误伤正常用户:

  • 有真实用户价值、攻击也集中(搜索页、列表筛选页):优先降级——限速 + 缓存 + 静态兜底,保留可用性。
  • 高频写库、业务必须(下单、短信、上报接口):限频 + 人机验证,必要时进入排队模式,不要直接关闭。
  • 明显无业务意义的路径:日志里出现大量随机字符串参数的请求,这类几乎全是攻击流量,直接按规则拒绝:
# 搜索参数是 20 位以上无意义随机串,直接静默断开(按实际日志特征调整)
if ($arg_s ~ "^[a-z0-9]{20,}$") { return 444; }

判断的核心原则是:降级保可用,封禁保资源。用户能用到的东西尽量降级,用户用不到的垃圾请求果断封。

Q:降级期间怎么兼顾用户体验和 SEO?

三条原则:

  • 核心页面不降级:首页、文章详情、下单流程保持可用,只砍搜索、评论、筛选这类非核心动态入口。
  • 错误页要”说人话”:标明原因和重试时间,配合 503 + Retry-After,让用户愿意等、让搜索引擎知道是临时故障。
  • 配置要有回滚开关:把降级指令单独写进一个 include 文件,恢复时删掉或改名再 reload,避免攻击结束后忘记切回、长期跑在降级状态。
include /etc/nginx/emergency/cc-degrade.conf;   # 应急开关:删掉此行即恢复

nginx -t && nginx -s reload                      # 应用降级
# 攻击结束后:注释或删除 include 行,再次 reload 即恢复原状态

Q:还有什么补充建议?

把 CC 应急动作固化成一个顺序,出事时照着做就行:① 看日志锁定被打入口 → ② Nginx 全局限速止血(10 r/s + 并发 20)→ ③ 高消耗动态入口降级(关闭 / 缓存 / 静态兜底)→ ④ 503 自定义错误页 + Retry-After → ⑤ 登录注册加人机验证 → ⑥ 恢复后按日志调参,把应急配置沉淀成模板

降级不是示弱,而是把资源优先分配给核心业务,这在 CC 场景下是最有效的保命手段。不过降级只是最后一道保险:长期来看还是建议在 CDN/WAF 层做频率识别、JA3 指纹与请求特征分析,让异常请求在边缘就被拦掉,源站只承接干净流量,这样才不用每次都靠”砍功能”来续命。