CDN 本来是帮着分担流量的,结果被爬虫刷一波,流量账单反而涨了——这种事在图片站、资讯站、电商站上都很常见。本文用 9 个问答讲清爬虫是怎么把 CDN 流量刷爆的、哪些行为最烧流量、怎么从日志里定位、Nginx 和 CDN 两侧分别怎么配,以及已经产生高额账单之后怎么止损。
Q:爬虫怎么会把 CDN 流量刷爆?原理是什么?
CDN 的成本模型是「缓存命中多就便宜」,而爬虫干的恰恰是相反的事。
- 制造大量未缓存 URL:带随机参数、翻页、筛选条件、时间戳的链接,每一条在 CDN 眼里都是独立资源,全都要回源取一次。
- 回源流量单价更高:CDN 节点回源占用带宽和连接,回源流量通常不计入免费额度,价格明显高于边缘流量。
- 大文件重复拉取:图片、视频、附件单文件体积大,一个爬虫反复拉几百遍,边缘流量直接上去。
- 回源队列堆积:同一时刻大量不同 URL 回源,源站被拖慢甚至返回 502,CDN 的重试机制会进一步放大请求量。
核心一句话:爬虫刷的不是「请求数」,而是「回源次数」。只盯着 QPS 看不出来,必须看回源率和缓存命中率。
Q:哪些爬虫行为最烧流量?
按危害排序,前四类占了绝大多数账单。
- 随机参数扫描:
?id=123&r=0.4832这种参数一看就是绕过缓存的惯用手法,每个参数值在 CDN 眼里都是新资源。 - 无边界翻页与日历页:从
/list?page=1一直爬到page=99999,越靠后的页面命中率越低,几乎全部回源。 - 站内搜索与筛选组合:搜索页天然不适合缓存(结果组合爆炸),爬虫把筛选条件当遍历入口,直接把源站压住。
- 大文件遍历下载:图片站、文档站、素材站最容易被这样刷。
- 补充一类:伪装成正常浏览器的分散爬虫,单 IP 看着很老实,几百个 IP 合起来就是洪水。
Q:怎么快速定位是谁在刷?日志里看哪几个字段?
先聚合,再看明细。关键是按「URL 模式 + 来源指纹」分组,而不是盯着单个 IP 看。
# 1. 找出请求最集中的 URL 模式(剥掉参数只看路径)
awk '{print $7}' /var/log/nginx/access.log | sed 's/?.*//' | sort | uniq -c | sort -rn | head -30
# 2. 统计带随机参数的请求量(绕过缓存的典型特征)
grep -Ec '[?&](r|_|t|cb|ts|_t)=[0-9]' /var/log/nginx/access.log
# 3. 按 UA 聚合,看是谁在刷
awk -F'"' '{print $6}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20
# 4. 按来源 IP 聚合(已还原真实 IP 的情况下)
awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20
三个判断信号:① 某个 URL 模式突然占了大头;② 缓存 MISS 比例明显高于平时(说明大量请求都是没缓存的新 URL);③ 单个 UA 或某段 IP 的请求量异常集中。定位到之后,先限速止血,再谈长期策略。
Q:Nginx 层怎么拦住随机参数导致的缓存穿透?
思路是把「无关参数」从缓存键里剥掉,让带随机参数的请求命中同一份缓存;同时对这类请求做限速。搜索引擎蜘蛛要单独放行,不能被一起限掉。
# 1. 只保留白名单参数,其余参数(含随机参数)直接忽略
map $args $cache_ok_args {
default "";
"~(?:^|&)id=([0-9]+)" "&id=$1";
"~(?:^|&)page=([0-9]+)" "&page=$1";
}
# 2. 用归一化后的参数做缓存键,让随机参数命中同一份缓存
proxy_cache_key "$scheme$host$uri$cache_ok_args";
# 3. 白名单之外的请求才限速:map 生成空 key,空 key 不计入限速
map $http_user_agent $limit_key {
default $binary_remote_addr;
"~*(Baiduspider|Googlebot|bingbot|YisouSpider|Sogou web spider)" "";
}
limit_req_zone $limit_key zone=crawler:10m rate=5r/s;
server {
location / {
limit_req zone=crawler burst=10 nodelay;
limit_req_status 429;
proxy_pass http://backend;
}
}
要点有两个:① 用 map 让白名单 UA 拿到空字符串 key,Nginx 对空 key 的请求不计入限速,实现「只限爬虫不限搜索引擎」;② limit_req 只能配在 server 或 location 层,不能塞进 if 块里,白名单判断必须在生成限速 key 之前完成,否则蜘蛛会被一并限掉,收录立刻受影响。
Q:CDN 侧该怎么配,才能既有缓存又防刷?
- 缓存规则按后缀和路径分级:图片、CSS、JS、字体这类静态资源设长缓存(30 天以上),并忽略 query string(除非参数确实影响内容)。
- 动态页与搜索页不缓存但限速:这类页面做缓存容易导致内容错乱,正确做法是回源限速加单 IP 连接数上限。
- 设置回源限速与回源并发上限:给单节点回源 QPS 设上限,宁可让爬虫拿到 429,也不要让源站被打穿。
- 开启防爬与频率防护规则:按 IP、UA、路径频率识别,命中后依次降速、人机验证、拦截。
- 配置回源白名单:源站只允许 CDN 回源 IP 访问,避免爬虫绕过 CDN 直连源站刷带宽——这一步经常被忘,但效果立竿见影。
- 开启流量封顶与告警:多数 CDN 支持带宽或日流量上限,达到阈值自动告警或停服,是最后的成本保险。
Q:已经刷爆了,产生高额流量账单怎么办?
按「止血 → 定责 → 协商」三步走。
- 止血:立即在 CDN 侧对异常路径做拒绝或限速,拉黑异常 UA 与 IP 段,必要时临时关闭最容易被刷的接口或页面。先止损,再分析,不要在账单还在涨的时候坐下来研究日志。
- 定责取证:把异常时间段的访问日志导出来,按 IP、UA、URL 聚合,形成一份带时间戳的证据(谁、什么时候、请求了什么、多少量)。这份材料是事后申诉和加固的依据。
- 协商:云厂商对异常流量通常有申诉通道,但是否减免完全取决于证据与政策,不要默认一定能免。同时评估是否切换成「流量封顶 + 按带宽计费」的套餐,把最坏情况锁定住。
事后必须做的事:把这次的异常特征写成拦截规则固化下来。否则下一波爬虫换个 UA 再来,你还得从头重来一遍。
Q:封禁会不会误伤正常用户和搜索引擎?
会,所以规则要有分级与观察期。
- 白名单先行:搜索引擎蜘蛛、自有监控、合作方出口 IP 全部加白,同时确认白名单是「只免拦截」还是「完全放行」。
- 分级处置:第一次命中只记录并降速,连续命中才升级到人机验证,再往上才封禁。不要一命中就直接返回 403。
- 封禁必须带过期时间:用 ipset 的 timeout 而不是永久黑名单,误封可以自动恢复。
- 盯误伤指标:上线后观察 429/403 比例、真实用户投诉、蜘蛛抓取量,任何一项异常都要回退阈值。
# 临时拉黑刷流量的 IP 段,1 小时后自动解封
ipset create cdnban hash:net timeout 3600 -exist
ipset add cdnban 198.51.100.0/24 -exist
iptables -I INPUT -m set --match-set cdnban src -j DROP
# 查看当前封禁列表
ipset list cdnban | tail -20
# 确认误封需要立刻放行
ipset del cdnban 198.51.100.0/24
Q:怎么提前预警,而不是月底看账单才知道?
做监控,三个指标缺一不可。
- 带宽与流量的分钟级突增告警:以过去 7 天同时段均值的 1.5 倍为基线,超了立刻通知,这是发现得最快的方式。
- 回源率与缓存命中率:命中率骤降通常先于账单暴涨出现,是很好的先行指标。
- Top URL 模式的日报:把「当日回源 Top 20 路径」做成每日报表,一旦出现陌生模式(比如全是搜索页、全是带随机参数的 URL),基本可以确定有人在刷。
Q:还有什么补充建议?
落地清单按顺序过一遍:① 源站只允许 CDN 回源 IP 访问 → ② 静态资源长缓存并忽略无关参数 → ③ 动态页与搜索页设置回源限速 → ④ 按 IP、UA、路径频率做防爬规则,命中先降速后升级 → ⑤ 设置带宽与日流量封顶 → ⑥ 监控带宽突增与命中率骤降 → ⑦ 把每次异常特征固化成规则。
补两个容易忽略的点。第一,爬虫最爱挑「长尾页面」下手——那些平时很少有人访问、缓存里没有、但数量巨大的页面(标签页、归档页、组合筛选页)。这些页面做缓存的价值最高、成本却最低,值得优先配规则。第二,不要让 CDN 成为唯一防线:CDN 挡住的是边缘流量,源站还得有一层应用级限速,否则一旦有人拿到源站 IP 直连,前面的配置会全部失效。
一句话总结:防爬虫刷流量,本质是「把请求收束到少数可缓存的形态」,再对收束不了的异常请求做限速与拦截。先降回源,再谈拦截,顺序反了就白花钱。