CDN 回源带宽突增 3 倍:排查发现是爬虫绕过了边缘缓存策略
CDN 回源带宽突增 3 倍排查发现是爬虫绕过了边缘缓存策略上周二早上 9 点运维群炸了。监控告警显示 CDN 回源带宽从前一天的 200Mbps 飙到 600Mbps源站 Nginx 连接数打满部分用户开始报 502。我第一反应是是不是上新版本了问了研发没有。那只能是流量异常。查了一圈发现不是 DDoS也不是正常业务高峰。真相藏在 CDN 日志里——有个爬虫在绕边缘缓存直接回源抓取静态资源。今天把这个排查过程完整拆出来包括监控定位、日志分析、缓存规则修复和后续的爬虫治理方案。一、先确认不是 DDoS是合法流量回源带宽突增时先别慌着封 IP。我按这个顺序排查1. 看源站连接数# 源站 Nginx 实时连接数ss-s|grepESTAB# 或看 nginx stub_statuscurlhttp://localhost/nginx_status连接数从平时的 800 飙到 4000但 QPS 并没有等比例增长。这说明大量连接是长连接或慢请求不是正常用户访问。2. 看回源流量的 User-Agent 分布# 从 CDN 回源日志里提取 UA假设日志格式是 Nginx 标准 combinedawk-F{print $6}/var/log/nginx/access.log|\sort|uniq-c|sort-rn|head-20输出里前 5 名全是搜索引擎爬虫Googlebot、Bingbot、Bytespider、YisouSpider、Sogou web spider。看着挺正常但再往下看第 6 名开始画风变了——一个自定义 UA 叫DataCrawler/1.0的占掉了 38% 的回源流量。3. 看请求路径# 提取这个爬虫的请求路径awk-F$6 ~ /DataCrawler/ {print $2}/var/log/nginx/access.log|\awk{print $2}|sort|uniq-c|sort-rn|head-10清一色是/api/v2/products?page1、/api/v2/products?page2…一直到/api/v2/products?page5000。一个爬虫在按页码遍历商品列表 API而且每次请求都带了不同的 query 参数。二、定位为什么 CDN 缓存没拦住它我们的 CDN 配置了边缘缓存静态资源JS、CSS、图片和公开 API 的列表页按理说是缓存 5 分钟的。但这个爬虫的回源率接近 100%。抓了一个实际请求头看# 从日志里还原一个请求curl-Ihttps://cdn.example.com/api/v2/products?page1234\-HUser-Agent: DataCrawler/1.0\-HAccept: */*返回的X-Cache-Status: MISS。进一步检查 CDN 控制台发现缓存规则里有一条带 query string 的请求默认不缓存。这是早期为了防止用户登录态透传出问题设置的保守策略结果现在成了漏洞——爬虫每换一页就是一个新 URLCDN 永远认为是新请求直接回源。三、修复分两步先止血再根治第一步紧急止血10 分钟1. 在 CDN 侧封 UA大多数 CDN 控制台支持 WAF 或访问控制里直接封 User-Agent。我们用的阿里云 CDN# 调用阿里云 CDN API 加 UA 黑名单aliyun cdn AddIpOrUaToWafGroup\--DomainNamecdn.example.com\--RuleTypeua\--RuleValueDataCrawler/1.0\--Actionblock2 分钟后回源带宽从 600Mbps 掉到 350Mbps源站压力明显缓解。2. 在源站限流兜底# Nginx 层面加按 UA 的限速 map $http_user_agent $crawler_limit { default ; ~*DataCrawler $binary_remote_addr; } limit_req_zone $crawler_limit zonecrawler:10m rate1r/m; server { location /api/v2/products { limit_req zonecrawler burst5 nodelay; # ... 原有配置 } }rate1r/m的意思是同一个 UA 来源 IP每分钟最多 1 个请求。对正常爬虫比如搜索引擎够用了对恶意遍历的直接按死。第二步缓存策略根治30 分钟封 UA 只是治标换一个 UA 就能再进来。真正要修的是缓存规则。问题是/api/v2/products?page1234这种带分页参数的 API到底该不该缓存我们的判断公开列表页可以缓存但时间要短1-3 分钟因为数据变化不频繁。带敏感参数如token、user_id绝对不许缓存。所以改 CDN 缓存规则按 query string 白名单处理# 在源站返回头里告诉 CDN 怎么缓存 location /api/v2/products { # 只缓存带 page/size 两个参数的其他参数回源 if ($args ~ ^(page\dsize\d)$) { add_header Cache-Control public, max-age120; } # 如果带了其他参数强制不缓存 add_header Cache-Control private, no-store; proxy_pass http://backend; }CDN 侧配合开启query string 缓存白名单只保留page和size其他参数不参与缓存 key 计算。改完后 10 分钟回源带宽稳定到 180Mbps比出事前还低一点。四、踩坑记录坑 1只封了 UA没改缓存规则第二天又炸了封 UA 当天晚上运维群又响了。这次 UA 变成了Mozilla/5.0 (compatible; DataCrawler/2.0)。改个版本号就绕过了。所以封 UA 只能做应急不能当长期方案。坑 2CDN 缓存白名单配反了阿里云 CDN 的 query string 缓存有两种模式保留指定参数只保留你列的参数参与缓存 key其他忽略。忽略指定参数把你列的参数忽略其他参与缓存 key。我一开始选成了忽略指定参数把page和size忽略掉结果/api/v2/products?page1和page2被当成同一个缓存 key用户翻页看到的数据全是第一页。坑 3源站Cache-Control和 CDN 控制台规则冲突源站返回了Cache-Control: private但 CDN 控制台又勾了强制缓存。最后 CDN 以控制台规则为准把不该缓存的 API 也缓存了。修复方案CDN 控制台取消强制缓存让源站 header 说了算。五、后续的爬虫治理方案这次事件暴露了两个问题我们不知道有多少爬虫在抓我们的 API。缓存规则太保守正常爬虫都能打穿。修复后补了两件事1. 加 robots.txt 和 rate limit 声明User-agent: * Crawl-delay: 2 Disallow: /api/v2/internal/ Disallow: /api/v2/orders/ Allow: /api/v2/products虽然很多爬虫不读 robots.txt但正经搜索引擎会遵守。至少减少了一半合法回源。2. 建一个 CDN 回源监控看板用 Grafana CDN 日志我们接入到了 SLS建了一个看板核心指标回源带宽按 5 分钟聚合回源率回源请求 / 边缘命中请求按 UA 的回源流量 TOP10按 URL 路径的回源流量 TOP10告警规则回源带宽 300Mbps 持续 5 分钟 → P1 告警回源率 30% 持续 10 分钟 → P2 告警单个 UA 回源流量占比 20% → P2 告警写在最后CDN 回源带宽突增不一定是攻击有时候只是一个不讲究的爬虫在按页码遍历你的 API。最麻烦的不是封它而是找出它为什么能绕过你的缓存——这次事件里保守的 query string 策略成了最大的漏洞。修完这套方案后我顺手把全站的缓存规则审计了一遍发现类似的带参数就不缓存规则还有 7 条。逐条改成白名单模式后整体回源带宽降了 40%。如果你也在用 CDN建议定期跑一下这个命令# 看最近 1 小时回源流量里有没有异常 UAawk-F{print $6}/var/log/nginx/access.log|\sort|uniq-c|sort-rn|head-20花 2 分钟可能能提前发现下一个DataCrawler。