有一次,某电商大促零点刚过,CDN节点上突然涌出大量回源请求,直接把源站带宽打满,页面加载变慢,领导在群里@我:“CDN没干活?”我查了半天,发现问题出在几个月前一次“无关紧要”的参数配置调整上——某个业务同学把URL中的?product_id=123改成了?pid=123,而CDN的“保留参数”白名单里写的还是product_id,于是所有带pid的请求都被当成“无参数”处理,缓存键只剩基础路径,结果同一商品的不同图片全都命中同一份缓存,内容混乱,用户投诉,前端加了时间戳强制刷新,直接引发回源风暴。
你看,一个“保留参数”的配置,看起来只是勾个选项或写个正则,实际上它能让你半夜爬起来debug,今天咱们就把这件事彻底聊透。
先搞清楚:CDN到底在“保留”什么?
CDN处理用户请求时,URL中的查询参数(query string,即问号后面的部分)是绕不开的东西,CDN在三个关键环节会用到这些参数:
- 缓存键(Cache Key)构建:判断两个请求是否是同一份资源,是否应该命中同一缓存。
- 回源请求转发:回源时把哪些参数传给源站。
- 边缘逻辑处理:比如通过边缘函数或规则引擎修改URL、添加Header等。
“保留参数”这个概念,通常出现在缓存键和回源请求两个层面,而且经常被混为一谈,但实践中,这两个场景的保留策略往往需要分家:缓存键可能只想保留“影响内容”的参数,而回源请求可能需要保留所有参数(比如广告追踪、日志打点)。

保留参数这个看似简单的配置,其实是CDN调优的修罗场
三套方案的博弈:全留、全删、挑着留
方案A:全部保留
- 做法:缓存键包含完整query string,顺序、大小写、值都参与哈希。
- 优点:没有“误伤”,参数里任何变化都会对应不同缓存,业务绝对正确。
- 缺点:缓存碎片化严重,一个URL带
?utm_source=google和?utm_source=facebook就是两份缓存,如果再来个?ref=home又是三份,实际上这些参数对响应内容毫无影响,纯属浪费。 - 典型翻车:某新闻网站,同一文章的不同分享渠道带来的请求,缓存了上百份相同内容,命中率低到30%,源站累死。
方案B:全部忽略
- 做法:缓存键只取路径,忽略所有query string,回源时也不带(或只带部分)。
- 优点:缓存命中率高,简单粗暴。
- 缺点:如果参数确实影响内容(比如
?product_id=123),那就出大乱子——所有商品都返回同一份数据。 - 适用前提:你必须100%确认所有参数都是“有它没它都一样”,比如纯静态资源加版本号(通常版本号放在路径里更合理)。
方案C:选择性保留(白名单/黑名单/正则)
- 做法:配置一组参数名或正则,仅将这些参数纳入缓存键,其余忽略,同时可以独立配置“回源保留参数”策略。
- 优点:兼顾缓存效率和业务正确性,是大多数场景的正确答案。
- 难点:需要精确理解业务逻辑——哪些参数真的影响内容?哪些只是追踪、统计、随机数?大部分时候,业务方自己也说不清,而且参数名可能不一致(
idvsproduct_idvspid),一旦更新不及时就出现前文的惨案。
到底什么时候该保留,什么时候该扔?
这不是一个技术问题,而是一个业务认知问题,我一般从两个维度来判断:
维度1:参数是否改变响应内容?
- 是 → 必须放进缓存键(保留或纳入自定义hash)
- 否 → 可以考虑忽略
但“是否改变内容”有时并不明显,比如一个搜索API,带?query=连衣裙&page=2&sort=price,其中query和page肯定影响结果,sort也影响,但?debug=true就不影响内容(它可能只在响应里加个请求ID header),这时debug参数就可以忽略,但注意——如果你强制忽略,那么一次带debug的请求可能会命中未带debug的缓存,导致看不到调试信息,这通常可以接受,因为debug是给开发用的,生产环境不要滥用。
维度2:参数是否是业务“外挂”的?
- 广告追踪(utm_source、utm_campaign)、渠道标记(ref、from)、埋点参数(_t、ts)等,绝大多数不影响响应内容,但业务需要它们在回源日志里被记录下来,这时候你的策略应该是:缓存键忽略,但回源请求保留,很多CDN支持分别设置,比如阿里云CDN的“忽略参数”功能可以开启“回源保留”,CloudFront的Behavior里也有“Query String Forwarding and Caching”两个独立选项。
选型建议:别当甩手掌柜
-
默认不要选“全部保留”,除非你的业务每一个参数都会改变响应,否则就是浪费,而“全部保留”往往是因为懒——懒得分析参数,结果缓存命中率惨不忍睹。
-
“全部忽略”仅限于明确场景:比如图片、字体、CSS/JS(且版本号已通过路径区分),或者你的后端设计本身就依赖URL路径而非参数,对于API类,千万别无脑忽略。
-
优先使用白名单:花半小时梳理所有URL中出现的参数,跟业务方确认哪些真正影响内容,如果参数数量庞大且杂乱,可以用正则匹配模式,比如
^(id|page|sort|filter)$,同时配置好“回源保留全部”或“回源保留自定义列表”。 -
警惕“参数顺序”和“值编码”:有些CDN默认对参数排序(如阿里云、腾讯云),有些则不排序,如果不排序,
?a=1&b=2和?b=2&a=1会被视为两个不同缓存键,造成额外碎片,建议开启参数排序功能;如果不行,就在边缘函数里统一排序后再构造缓存键。 -
利用边缘函数做精细控制:当CDN原生配置无法满足时(比如需要根据参数值动态判断是否保留),可以用Edge Function或Lambda@Edge,对
?experiment=A保留,但对?experiment=B忽略——这通常用于A/B测试场景,需要非常小心,因为保留实验参数会切割缓存,影响普通用户命中率,更优的做法是用Cookie或Header携带实验标识,而非URL参数。 -
监控和回滚:改了“保留参数”策略后,务必监控缓存命中率、回源带宽、源站错误率,由于这个变更的影响往往不是即时的(缓存需要逐步过期),建议灰度发布,并保留修改记录,我见过最惨的案例是:某公司把“全部保留”改为“白名单”,结果漏掉了
?version这个参数,导致新版CSS被老版缓存覆盖,线上样式错乱了一小时。
最后说句大实话:没有完美的默认配置,每个参数背后都有一条业务规则,规则变了,配置就得变,当你下次在CDN控制台看到“忽略参数”或“保留参数”这个选项时,请把它当成一个重要的架构决策,而不是一个随手打勾的开关,毕竟,你永远不知道下一个深夜电话会是因为哪个参数惹的祸。
还没有评论,来说两句吧...