2026 年 6 月 12 日早上,我在同一个小时内收到 4 份 Search Console 报告:414 个 4xx 状态码、15 个缺 meta description、34 个标题过长、15 个缺 h1 标签、2 个页面有多个 h1 标签。合计 480 个被标记的问题。
大多数人会慌。默认反应是"删掉问题页、重写标题、补 h1 标签"。但这个直觉对 480 个问题中至少 30 个是 完全错误的。这是我如何用 4 步决策树在 30 分钟内穿透 4 份报告、只修必要的部分、让系统对未来的同类问题免疫的故事。
| 报告 | 数量 | 第一直觉 | 正确答案 |
|---|---|---|---|
| 4xx 状态码 | 414 | 重写丢失的页面 | 用 Contents API 把 414 篇从 archive/ 迁回根路径 |
| 缺 meta description | 15 | 给 15 个都加上 | 15/15 都是 301 跳转页,不动 |
| 标题过长 | 34 | 重写全部 34 个标题 | 5 个是 Moved 页,29 个是真的(只修 29 个) |
| 缺 h1 | 15 | 给 15 个都加上 h1 | 15/15 都是 Moved 页,不动 |
| 多个 h1 | 2 | 删掉多余的 h1 | 在 2 篇真实文章中把 6 个 h1 降级为 h2 |
没有系统化的方法,你一定会误修至少其中一个类别。这个决策树把 480 个让人恐慌的标记变成 31 个真实修复 + 449 个已知可忽略项。
动手之前,先把每个 URL 分到 5 个桶之一:
1.6-1.9 KB + 2026-05-18 mtime + <title>... — Moved</title> + <meta http-equiv="refresh">Moved 跳转页的指纹是 金子。如果你做了 5/17 批量重命名,把 16 个短 slug 变成 SEO 友好的长 slug,旧 URL 就会被保留为 301 跳转。它们 本来就应该是小文件。它们 本来就该缺 description 和 h1。它们 在按设计工作。
对每个 URL,用 curl -sS -o /dev/null -w "%{http_code}" 探一下。确认 HTTP 200。如果你看到 4xx,不要慌——这个 4xx 可能是 CDN 边缘缓存,不是真实状态。这正是第 3 步要解决的问题。
这是 SEO 工具箱里最被低估的技巧。当腾讯云 → GitHub Pages 的 CDN 边缘不稳定时,你的 curl 拿到 404 或 503,但文件其实在仓库里。要消除假阴性:
curl -sS "https://api.github.com/repos/you/you.github.io/contents/path/to/file.html" | python3 -c "import sys,json,base64; d=json.load(sys.stdin); print(base64.b64decode(d['content']).decode()[:200])"
Contents API 走 GitHub 主干,不走你的 CDN。它告诉你 仓库的实际状态,绕过那个对 curl 说谎的缓存层。这一个技巧就把"414 个文件丢了"的报告变成了"0 个文件真的丢了——309 个在 main archive/,105 个在 affiliate-blog/"。
现在你把每个问题归类成两个动作之一:
应用决策树后:
4 步决策树是 治标。治本是让系统对未来同类问题免疫。对"标题过长"这个类别,治本是改 generate-html.py 强制加限制:
def _limit_title_for_seo(title, suffix=' - TechPassive', max_total=70):
"""硬限制 title 长度,防止未来再产生标题过长报告。"""
clean = re.sub(r'<[^>]+>', '', title).replace('"', '"').strip()
if not clean:
return clean
full_len = len(clean) + len(suffix)
is_cn = bool(re.search(r'[\u4e00-\u9fff]', clean))
effective_max = 75 if is_cn else max_total
if full_len <= effective_max:
return clean
budget = effective_max - len(suffix)
cut = clean[:budget]
for sep in [' - ', ' — ', ', ', ': ', ':', ',', '、', ' ']:
idx = cut.rfind(sep)
if idx >= budget * 0.6:
cut = cut[:idx]
break
return cut.rstrip(' ,:—-—')
一个函数,元数据字典里一行:'title': _limit_title_for_seo(title)。从今往后,每篇生成的文章在 HTML 写入前都会把 title 截断到 ≤70 字符(中文 ≤75 字符,因为 Google 对中文的截断阈值不同)。这个报告类别 永久退役。
Search Console 报告不是你的问题。你的问题是 报告说和页面实际是什么之间的鸿沟。在你建立一个桥接这个鸿沟的决策树之前,每份批量报告都会触发一次恐慌-编辑循环,破坏的比修复的多。
4 步决策树不针对任何单一报告类别。它适用于:
一旦你有了这棵树,每一份未来报告都是 30 秒查询。第一次建立成本高。后续每次成本是零。
在下一封 Search Console 邮件落地前要做三件事:
.moved-pages-manifest.json 列出每个 301 跳转页的 target URL 和 mtime。每次批量重命名时更新它。/tmp/:30 行 Python 包装 GitHub Contents API 做单文件推送。外科手术式修复不要用全量流水线脚本——它们会触发 IndexNow 和 15 篇文章推送,纯粹是噪音。投入是一个下午。回报是永久免疫自托管博客最常见的 SEO 恐慌循环。
← 返回首页