死链处理方法:一次小流量灰度如何暴露全量发布的例外

📍 WDQWDWQD987AAAAA:216.73.216.139
📱 Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; ClaudeBot/1.0; +claudebot@anthropic.com)
🔗 /ebfc5cad7936.html
📄

死链处理方法:一次小流量灰度如何暴露全量发布的例外

灰度只放 1% 流量时,死链处理看起来是干净的:返回码正确、跳转生效、日志没有异常。问题出在灰度没有覆盖的那部分 URL——它们往往依赖另一套路由、另一批参数或另一层缓存。全量发布后,这些例外才第一次被真实用户触发。要解决的不是“怎么再跑一遍灰度”,而是先把灰度样本与全量 URL 集合做一次差集,找出被排除在外的模式。

先确认灰度到底测了哪一批 URL

多数灰度按流量比例或用户分桶放量,而不是按 URL 模式放量。这意味着灰度命中的是“一部分访问”,不是“一部分链接”。如果死链只集中在某类 URL 上,比如带分页参数的列表页、旧域名下的静态页、或某个子目录的详情页,按流量分桶的灰度可能一次都没碰到它们。

可执行动作:导出灰度期间实际命中的 URL 列表,与全量发布后会暴露的 URL 列表求差集。差集里如果出现成片的路径前缀或参数模式,那批就是全量才暴露的例外。这个动作的结果决定下一步:差集为空,说明问题不在覆盖面,应转向缓存或 CDN 层;差集不为空,处理对象就锁定在这批模式上。

用一小段真实链接替换“抽样推断”

灰度报告常写“抽样 200 条,全部正常”。抽样正常不能推出全量正常,因为抽样通常来自站点地图或导航,而站点地图不保证收录,也不代表用户真实点击的入口。真正会暴露死链的,是站内搜索结果、旧外链、结构化数据里的 URL、以及用户收藏的历史地址。

假设某站有 5 万个 URL,灰度只验证了导航里的 200 条。若旧文章正文里嵌的链接有 3000 条,且其中 4% 指向已删除页面,那么全量发布后大约会出现 120 条死链。这个数字只是用来说明比较方法的假设,不是任何真实项目的测量结果。它的意义在于:抽样集合和风险集合不重合时,抽样通过率再高也不能作为放行依据。

把例外拆成三类,分别给处理方式

差集里的 URL 通常落在三种情况,处理方式不同,混在一起会反复返工。

动作与结果:给每类打上标记后再批量配置。配置完成后,先只对差集里的 URL 复测返回码,而不是全站复测。差集通过,才说明这次灰度遗漏的例外被补上了;差集仍有失败,说明分类里还有没识别出的模式。

灰度通过后仍要盯的例外来源

即使差集处理完毕,全量发布后仍可能出现新的死链,来源往往不在代码里,而在发布动作之外。

  1. CDN 或反向代理缓存了旧规则下的响应,灰度节点与全量节点缓存策略不一致时会表现不同。
  2. 大小写、结尾斜杠、URL 编码方式在不同入口下被规范化成不同地址,只有真实入口才会触发。
  3. 旧域名或旧协议的链接通过外部引用进入,灰度流量里根本不包含这些来源。

这里要区分现象与结论:发布后死链日志数量下降,可能只是爬虫还没重新访问,也可能是日志采样变了,不能单独证明处理正确。同理,请求量归零既可能是 410 生效,也可能是入口被断开。要判断处理是否真的生效,需要同时看返回码分布和这些 URL 是否仍被引用。

把这次例外写回下一次的检查项

这次暴露的例外,价值在于变成下一次灰度前就能核对的清单。把差集里出现的路径前缀、参数名、入口来源记下来,下次发布前先确认灰度是否覆盖了这些模式。如果灰度机制本身只能按流量分桶,那就额外准备一小批真实入口 URL 做人工验证,成本远低于全量发布后回滚。

死链处理在这里的关键判断是:灰度通过只证明被抽到的那部分没问题,不能证明全量没问题。先做差集、再按三类分别配置、最后只复测差集,这套顺序能让一次小流量灰度真正起到拦截作用,而不是给全量发布提供一个看起来安全的假象。

图1 图2

nginx