上个月一个做跨境电商投放的团队找到我,说他们独立部署的分流节点带宽费用突然涨了将近一倍,运维第一反应是「被CC攻击了」,加了一堆IP黑名单结果没什么用。我让他们把Nginx的access日志按请求类型拆开看,发现真实访客请求量没怎么变,但回源请求占比从平时的两成出头涨到了六成还多。回源流量占比过高这个问题,在斗篷系统的日常运维里其实比想象中常见,而且它和攻击的特征差异很明显——攻击通常伴随并发连接数飙升和单IP高频,回源升高则表现为缓存层形同虚设,请求一层层穿透到后端。本文就从这个误判切入,把回源升高的三类成因和收敛配置路径讲清楚。
先搞清楚:回源升高和「被刷」的特征差异
很多人一看到带宽账单跳涨就往攻击方向想,这个直觉可以理解,但判断依据不够。
攻击流量的典型特征是:单IP或少数IP段请求密度极高、请求路径集中、UA往往重复或缺失、并发连接数在短时间内陡增。你在Nginx日志里看到的是「同一个来源反复打同一个URL」。
回源流量升高的特征完全不同:访客请求总量基本平稳,但缓存命中率往下掉。具体表现为X-Cache响应头里MISS的比例大幅上升,后端PHP-FPM的活跃进程数持续偏高,upstream_response_time拉长。说白了,请求还是那些请求,只是本该在缓存层被拦住的,现在全放过去了。
有个简单的判断方法:在Nginx配置里给缓存命中和回源分别打上不同的$upstream_cache_status标记,按小时统计MISS占比。如果MISS占比从两成涨到六成,而总请求量波动在百分之十以内,基本可以排除攻击,往缓存配置方向查。
三类常见成因:从缓存键到动态参数
回源升高不是一个孤立现象,背后通常对应三类配置问题。
缓存键设计粒度过细
这是最常见的一类。缓存键如果包含了完整URL,而投放链接上又挂了一堆跟踪参数,比如utm_source、gclid、fbclid这些,每个访客带来的参数组合都不同,缓存层就会认为这是不同的资源,全部回源。
我见过一个案例,同一个落地页因为参数顺序不同被缓存成了十几个版本,命中率自然上不去。
动态判定逻辑未做结果缓存
斗篷系统的分流判定需要读取UA、Referer、IP归属等信息,如果每次请求都重新跑一遍完整判定链,后端压力会很大。合理的做法是把判定结果按会话维度缓存起来,比如用Cookie或Redis记录该访客命中的页面版本,后续请求直接读缓存结果。
关于判定链的时序设计,可以参考分流判定链路拆解,里面讲了从请求到返回的完整决策过程。
缓存过期策略过激
有些团队为了「保证数据新鲜」,把缓存TTL设得很短,比如30秒或1分钟。对于页面版本这种变化频率不高的内容,过短的TTL等于没有缓存。
回源收敛的配置步骤
定位到成因之后,配置调整按以下顺序来。
- 收敛缓存键:在Nginx里用
proxy_cache_key指令只保留路径和必要的版本标识,忽略跟踪参数。比如设为$scheme$host$uri$is_args$cookie_page_version,把参数和Cookie里的版本标识作为键的一部分,而不是把所有query string都塞进去。
- 对判定结果做二级缓存:在PHP层把分流判定结果写入Redis,key用访客的会话标识,value存页面版本号,设一个合理的TTL比如15到30分钟。后续请求先查Redis,命中就直接返回对应版本,不用重跑整个判定链。
- 调整缓存TTL分层:静态资源可以设长一些,比如几小时;页面版本根据投放节奏设,一般10到30分钟够用。如果某段时间在频繁调版本,可以临时缩短,调完再恢复。
- 加回源限流兜底:在Nginx的
location块里配limit_req和limit_conn,防止某个参数组合被高频请求时把后端打满。
配置调整之后,观察窗口建议至少覆盖一个完整的投放时段。重点看两个指标:缓存命中率是否回到正常水位,以及后端upstream_response_time的P95是否下降。
一个容易忽略的联动问题
回源收敛做完之后,有可能会遇到另一个问题:页面版本切换不及时。因为缓存键和TTL变了,新版本发布后老缓存还没过期,部分访客仍然看到旧版本。
这个和缓存更新与UA配里描述的版本错配场景是同一类问题。处理办法是在版本发布时主动清除相关缓存键,而不是等TTL自然过期。Nginx可以用proxy_cache_purge模块做定向清理,或者在应用层维护一个版本号,发布时递增版本号让缓存键整体失效。
那位跨境电商客户最终把回源占比从六成压回到了两成五左右,带宽费用回落,后端进程池压力也明显减轻。他们踩的坑主要是缓存键没收敛,加上判定结果每次都重算,两个问题叠加把回源推高了。
小结
回源流量占比升高,优先排查缓存键设计、判定结果缓存和TTL策略这三块,而不是一上来就怀疑攻击。配置调整的顺序建议从缓存键收敛开始,再到判定结果二级缓存,最后调TTL和加限流兜底。调整后注意版本切换的及时性,必要时用主动清理代替被动过期。
常见问题
回源流量占比多少算正常?
这个要看情况。如果落地页内容以静态为主、参数结构简单,回源占比控制在两到三成比较合理。如果页面版本切换频繁或者判定逻辑复杂,回源占比可能会高一些,四成左右也能接受。关键是看趋势,突然从两成跳到六成肯定有问题。
缓存TTL设太短会导致回源升高吗?
会的,而且这个原因容易被忽略。TTL设成30秒或1分钟,等于缓存基本不起作用,每个请求都要回源。页面版本这类变化不频繁的内容,TTL设10到30分钟比较合适。顺带说一句,我一般还会在版本发布时主动清缓存,这样不用为了及时生效而把TTL压得太短。
回源升高会影响落地页加载速度吗?
会,而且影响路径很直接。回源意味着请求要穿透到后端,走一遍完整的判定和渲染流程,响应时间自然比直接命中缓存长。访客感知到的就是页面打开变慢,尤其在移动端网络条件下更明显。如果后端进程池被打满,还可能出现502或排队等待。
延伸阅读
回源收敛和缓存配置调整完之后,如果想进一步对比不同工具方案在缓存层支持上的差异,可以看看这篇:百度斗篷工具缓存能力对比