meta refresh 是在 HTML 的 head 里用 meta 标签做跳转的老写法,改版、移动端适配、旧链接收口时仍会用到。它和 301、302 最明显的区别是:服务器返回的状态码通常还是 200,浏览器和蜘蛛都要先拿到并解析 HTML,才知道后面还藏着一个新地址。这个差异会改变抓取路径的形态。
为什么这类跳转容易被漏掉
在服务器日志里,请求 /old-page 得到的是 200,响应体完整、字节数也不小,看起来一切正常。只有把 HTML 取出来看,才会发现 head 里躺着一行 refresh。如果巡检只看状态码和响应大小,这类入口基本不会暴露。
更麻烦的是,这类页面往往还带着一套完整的导航和内链,蜘蛛抓完正文、顺着链接走一圈,才在解析阶段发现跳转目标,等于多花了一次抓取成本。
常见的几种表现
状态码为 200,抓取路径在原地打转
旧地址被当成一个正常页面抓取,随后再根据 refresh 发现新地址。若站内仍有链接指向旧地址,两个地址会长期并存,重复抓取和入口分散同时出现。
跳转目标与 canonical、内链指向不一致
页面 refresh 指向 B,但 canonical 写的是 A,站内导航链的又是 C。蜘蛛拿到的信号互相矛盾,最终哪个地址被当作主入口,往往取决于抓取顺序,难以稳定。
延时值不是 0
写成 content="5;url=..." 时,解析和跳转之间存在等待。对蜘蛛来说,这段等待没有意义,却会让页面看起来像一个停留时间偏短的正常页面,进一步模糊判断。
跳转目标本身又是 refresh 页面
旧链接改到中间页,中间页再 refresh 到新链接,形成两跳以上。每跳都返回 200,链路越长,损耗越难在日志里直接看出来。
排查顺序
- 先从日志里筛出返回 200、但 URL 带明显旧路径特征的地址,例如合并前的栏目、带 index_old、带临时后缀的链接。
- 用抓取工具或直接请求,检查响应体中是否存在 meta refresh,记录 refresh 的目标和延时值。
- 把 refresh 目标、页面 canonical、站内指向该地址的链接三者放在一起对照,标出不一致的页面。
- 顺着 refresh 目标再请求一次,确认目标地址返回的是 200 还是又一次 refresh,判断链路长度。
- 统计这些旧地址被内链引用的情况,确认是否还有页面在持续把它们重新暴露给蜘蛛。
处理建议
- 能用服务端跳转的,就换成 301 或 302。状态码明确,蜘蛛不需要解析 HTML 就能得到结论,也省下一次正文抓取。
- 确需保留页面的,把 refresh 延时设为 0,并让 canonical 与跳转目标保持一致,避免信号冲突。
- 跳转链路尽量压到一跳,中间页如果能合并就直接合并,减少无意义的入口节点。
- 站内引用统一改成最终地址,让旧地址只承担跳转职责,不再出现在导航、列表和正文链接里。
- 上线后复抓一批样本,确认旧地址的状态码已经变化,且没有新的 200 型 refresh 页面在产生。
巡检时把“返回 200 就当正常”这个默认假设放一放,很多路径断点并不在状态码上,而是藏在响应体的前几十行里。