重定向规则膨胀到几千行,拖慢构建、爬虫绕圈

_redirects 文件三千多行,构建变慢、爬虫追多跳链路,超过托管平台上限的规则还会被悄悄丢掉。讲清楚怎么审计、压平链路、安全裁剪。

最快的修法: 把每条规则解析到它的最终目标,把多跳链路改成直接指向终点(只跳一次),再删掉那些源 URL 连续 12 个月以上 0 曝光、0 点击、0 内链、0 外链的规则。光这一步通常就能砍掉一半文件,顺带消掉软 404 风险。如果你用的是 Cloudflare Pages,这件事更紧急:_redirects 文件上限是 2,000 条静态 + 100 条动态 = 2,100 条规则(2026 年 6 月核实),而一个 3,200 行的文件意味着超出上限的规则会被悄悄忽略——也就是说你以为在”重定向”的某些规则其实根本没生效。

两年前 _redirects 只有 40 行,现在已经 3,200 行了。每次 slug 改名、每次分类重整、每次发布后才发现的拼写错误,都加一行,从来没人删。结果构建多花好几秒解析文件,边缘托管平台悄悄丢掉超过上限的规则,Search Console 里 “Page with redirect” 桶越涨越大——因为 Googlebot 在追 3 跳链路,最后一跳还是 410。重定向表本身已经变成了一笔技术债。

这篇文章讲清楚膨胀是怎么发生的、怎么安全审计和压平,以及哪些规则能防止它再次堆起来。

先搞清楚你所在平台的硬上限

审计之前,先确认你的托管平台到底强制了什么。真正关键的不是”文件大不大”,而是”文件有没有超过上限、导致规则被丢掉”。以下数据 2026 年 6 月核实:

平台_redirects 规则上限匹配顺序超限行为
Cloudflare Pages2,000 条静态 + 100 条动态 = 共 2,100 条;每行 <= 1,000 字符;静态规则必须排在动态规则前面最靠上的匹配规则生效超过上限的规则被丢弃;溢出部分挪到账号级别的 Bulk Redirects
Netlify没有硬性条数,但 _redirects + netlify.toml 序列化后的输出太大会导致部署失败;10,000+ 条建议改用通配符/占位符或 Edge Functions从上到下,首条匹配生效部署失败,或该规则被忽略

一个常见误解:“_redirects 格式在 Cloudflare 上取最后一条匹配规则。“并不是。对于两个平台上的 _redirects 文件,都是最靠上的匹配规则生效。“取末条”那种行为只会出现在你手写 Cloudflare Worker 重定向逻辑时。所以如果有重复的源,文件里靠上的那条才是生效的。

常见原因

按命中率从高到低排列。

1. 每次改 slug 都加了一条”向前”规则,从来没合并过

编辑把 /why-claude-is-better/ 改成 /claude-vs-gpt-comparison/,加了一条重定向。半年后又改成 /claude-4-vs-gpt-5/,又加一条。现在变成 3 跳:第 1 跳 -> 第 2 跳 -> 第 3 跳 -> 200。

怎么判断: 挑几个源 URL 跑 curl -sIL <old-url>,如果最终 200 之前出现多个 Location 头,就是链路在跳。

2. 一次分类大改一口气塞了几百条

/category/ai-tools/ 改成 /ai-applications/,这个 PR 一口气加了 400 条重定向,覆盖所有子 URL,但没有一条收成通配符。

怎么判断: grep -c "^/category/ai-tools" _redirects。如果数量在几百,而且每一行都指向 /ai-applications/ 下的对应子路径,那一条通配符就能全部取代。

3. 给 Google 早就忘掉的 URL 写的重定向

2023 年给一个总共 12 次访问、18 个月没被爬过的 URL 加了重定向。规则到今天还在每次部署里。

怎么判断: 把重定向源路径跟过去 12 个月的 Search Console 加站内分析交叉对比。0 曝光、0 点击的源就是死重量。

4. 斜杠、www、协议归一化全塞在重定向表里

/foo/foo/http://https://www.、非 www. 每个组合都给每个页面单独写了重定向行。等于每个 URL 最多 6 条规则,本该在 host 层一条解决。

怎么判断: 同一个目标 URL 出现 4-6 次,源只差斜杠或 host。

5. CMS 迁移时遗留下来、没人认领的旧规则

从 WordPress 迁出来时,把整个重定向插件的数据库导出来塞进去了。600 条”上一代上一代 CMS”的 URL,过去 5 年没有任何链接指向它们。

怎么判断: 源路径不符合当前 URL 结构(/?p=1234/index.php?cat=.../wp-content/...)。

6. 重复规则,目标还不一样

同一个源路径出现两次,目标不一样。在 _redirects 文件里最靠上的那条生效(见上面的表),所以下面那条其实是死规则,还会误导以后改它的人。

怎么判断: awk '{print $1}' _redirects | sort | uniq -d

7. 软 404:全部重定向到首页

页面删了,有人把它 301 到 /。结果几百个毫不相关的 URL 都跳到首页。Google 把这些识别成软 404,在 Search Console 里报 “Submitted URL seems to be a soft 404”。已删且没有对应页面的 URL 应该返回 410 Gone(或 404),而不是跳到 /

怎么判断: grep -E " / 30[12]$" _redirects | wc -l,如果不只是个位数,就是模式不是例外。

我属于哪一类?

你实际看到的症状最可能的原因修复章节
有些重定向在生产环境就是不生效文件超过平台上限(Cloudflare 2,100),规则被丢上限表 + 第 4/5 步把文件压到上限以下
curl -sIL 显示 2 个以上 Location多跳链路(原因 1)第 3 步
几百个目标 URL 完全一样分类批量改名(原因 2)第 4 步
几百行 ... / 301软 404 跳首页(原因 7)改成返回 410
uniq -d 列出很多源重复且冲突的规则(原因 6)去重,保留最靠上那条
Search Console 里 “Page with redirect” 在涨链路和死规则在耗爬取预算第 3 + 5 步

最短修复路径

第 1 步:快照,版本化保留

动手前先把 _redirects 拷一份带日期的备份,方便对比和回滚:

cp public/_redirects public/_redirects.snapshot-$(date +%Y%m%d).txt
git add public/_redirects.snapshot-*.txt
git commit -m "snapshot: redirect map before audit"

第 2 步:把每条规则解析到最终目标

把每条重定向一路追到底,写成 TSV。这是整个清理过程里最有用的一份产物。

// scripts/resolve-redirects.mjs
import fs from 'node:fs';
const lines = fs.readFileSync('public/_redirects', 'utf8').split('\n');
const map = new Map();
for (const line of lines) {
  const m = line.match(/^(\S+)\s+(\S+)\s+(\d+)/);
  if (m) map.set(m[1], { to: m[2], code: m[3] });
}
function resolve(path, hops = 0) {
  if (hops > 10) return { final: path, hops, loop: true };
  const rule = map.get(path);
  if (!rule) return { final: path, hops };
  return resolve(rule.to, hops + 1);
}
for (const [from] of map) {
  const r = resolve(from);
  process.stdout.write(`${from}\t${r.final}\t${r.hops}\t${r.loop ? 'LOOP' : ''}\n`);
}

任何 hops > 1 都是要压平的链路;任何 LOOP 是 bug,马上修(死循环会给用户返回 “too many redirects”,在 Search Console 里报重定向错误)。

第 3 步:一次性把链路压平

把每条多跳规则直接改成指向最终目标,变成只跳一次。Google 的爬虫最多会跟 10 跳,但 Google 自己的建议是链路不超过 3 跳、越短越好(见 301 重定向官方文档);对真实用户而言,每多一跳大约多 150-300 毫秒延迟。压平同时也消掉了”链路尾巴是已删页面”造成的软 404 风险。

第 4 步:批量规则改成通配符

Cloudflare Pages 和 Netlify 都支持 splat 通配。源里用 *,目标里用 :splat,400 条同级重定向收成一条:

# 改之前
/category/ai-tools/chatgpt        /ai-applications/chatgpt        301
/category/ai-tools/claude         /ai-applications/claude         301
# ... 还有 398 条

# 改之后
/category/ai-tools/*              /ai-applications/:splat         301

在 Cloudflare Pages 上,splat 规则算作动态重定向(上限 100 条),而且必须排在所有静态规则后面。所以把批量规则收成 splat,既缩小了文件,又帮你守住了静态规则的上限。删原始规则之前,先用 curl -sI 抽 5-10 个样本路径验一下。

第 5 步:0 流量 + 0 内链的规则直接淘汰

把重定向源跟以下四类信号对比:

  • Search Console:12 个月内有曝光吗?
  • 站内分析:12 个月内有会话吗?
  • 内链:站内还有任何地方链向这个 URL 吗?
  • 外链:外链工具里还有引荐来源吗?

四项全是”否”,且规则超过 12 个月,直接删。这个 URL 已经从可索引网络里消失了,反正被索引的只会是最终目标页,这条规则什么都赚不到。

第 6 步:把归一化挪出重定向表

斜杠、host、协议这种归一化应该在框架或边缘配置里做,不是逐页写规则。Astro 里设一次策略即可:

// astro.config.mjs
import { defineConfig } from 'astro/config';

export default defineConfig({
  trailingSlash: 'always',
  site: 'https://example.com',
});

然后把 www 对非 wwwhttphttps 用边缘层的一条 host 级规则(Cloudflare 重定向规则,或 Netlify 的单条规则)解决,再把 _redirects 里每一条逐页的斜杠和 host 变体都删掉。

第 7 步:加上行数上限和”有效期”

CI 加一道检查:_redirects 超过行数上限就让构建失败。把上限设在平台硬上限之下,这样你在 CI 里就发现问题,而不是在生产环境:

// scripts/check-redirects.mjs
import fs from 'node:fs';
const MAX_LINES = 800; // 远低于 Cloudflare 的 2,100 硬上限
const lines = fs.readFileSync('public/_redirects', 'utf8').split('\n').filter(Boolean);
if (lines.length > MAX_LINES) {
  console.error(`Redirect map has ${lines.length} lines (cap: ${MAX_LINES})`);
  process.exit(1);
}

配合一条注释约定:每条规则上方都带 # added: YYYY-MM-DD reason: ...。每年一次例行审计,把陈旧的清掉。

怎么确认已经修好了

  • 对之前是链路源头的几个 URL 跑 curl -sIL <old-url>,现在 200 之前只有一个 Location 头。
  • wc -l public/_redirects 稳稳低于平台上限(也低于你的 CI 上限)。
  • awk '{print $1}' public/_redirects | sort | uniq -d 没有任何输出(没有重复源)。
  • 重新部署后,抽查 5-10 条你压平过的重定向:每条都返回 301 并直达最终 URL。
  • 接下来几周,Search Console 里 “Page with redirect” 的数量不再上涨。

哪些不该算在你头上

重定向表本质是过去若干年决策的账本。一部分膨胀是合法 URL 卫生的代价。目标不是”零规则”,而是”可审计、不超上限、且无多跳链路”。如果业务确实四次重命名了大栏目,你就会有几百条规则,这没问题,只要每条链路已经压平、每条规则都有理由。

容易误判成

  • “构建慢是 MDX 的锅。“——其实重定向表解析往往才是热点,先 profile 再下结论。
  • “内容页爬取预算不够。“——Googlebot 报一堆 “Page with redirect” 时,它把预算花在你的重定向表上,不在新内容上。
  • “边缘函数内存上限要升级。“——如果函数冷启动时把 _redirects 整个加载到内存,缩文件比升运行时便宜得多。
  • “某条重定向突然不工作了。“——在 Cloudflare Pages 上,很可能是文件越过了 2,100 条,平台把溢出部分丢掉了;缩文件,或把批量规则挪到账号级别的 Bulk Redirects。

预防

  • 加新重定向的同一个 PR 里,把多跳链路压平(写个脚本,不要靠人记流程)。
  • 分类批量改名一律用通配符,不要手工枚举同级。
  • 每条规则注释 # added: YYYY-MM-DD reason:,以后才知道能不能删。
  • 季度任务:把超过 12 个月、0 曝光、0 点击、0 内链、0 外链的规则全删。
  • 协议、host、斜杠的归一化挪到框架或边缘配置,不要逐页写。
  • CI 行数上限设在平台硬上限之下,文件不能悄悄涨过去。

FAQ

  • 删旧重定向会不会伤 SEO? 只有当源 URL 还有曝光、内链或外链时才会。四个信号连续 12 个月以上都是 0 的,这个 URL 已经不在可索引网络里了(反正被索引的只会是最终目标页),规则就是死重量。
  • slug 改名用 301 还是 302? 长期保留的改名用 301(永久),Google 会把排名信号传给目标页,并把旧 URL 移出索引。302 只在真临时移动时用,内容站很少遇到。
  • 重定向本地能用、生产不行,为什么? 在 Cloudflare Pages 上,_redirects 上限 2,100 条且会悄悄丢掉溢出部分,而且 splat(动态)规则必须排在所有静态规则后面。先检查行数和规则顺序。
  • Google 会跟几跳重定向? 一条链路最多 10 跳,但 Google 建议不超过 3 跳、最好少于 5 跳。能压成一跳就压成一跳。
  • 已删页面该不该重定向到首页? 不该。没有对应页面就返回 410 Gone(或 404)。大量跳到 / 会被判成软 404,在 Search Console 里被报出来。

相关

标签: #内容运营 #排查 #SEO #redirects #site-performance #抓取预算