---
title: "站内 SEO 审计脚本"
description: "一个构建后运行的脚本，检查静态站点里那些浏览器看着正常、爬虫看着是坏的问题。"
canonical: https://yonnia.com/projects/seo-audit
language: zh
status: active
year: 2026
stack: ["Node.js"]
repo: https://github.com/yonnia/yonnia.com
---
# 站内 SEO 审计脚本

一个构建后运行的脚本，检查静态站点里那些浏览器看着正常、爬虫看着是坏的问题。

这个网站构建流程的一半。跑在 `dist/` 上，检查一类特定的缺陷：在浏览器里完全正常，但爬虫看到的是坏的。

## 它检查什么

按「出问题的代价」分成失败和警告两级。失败会让 CI 退出非零。

**每个页面：**

- canonical 是否存在、是否绝对、是否在本站域名下、是否和文件实际的路径一致
- 两个页面是否声明了同一个 canonical——这正是 canonical 标签本该防止的事故
- `og:url` 是否和 canonical 一致（不一致时分享出去的链接和索引的链接是两个）
- 是否恰好一个 H1
- title 和 description 的长度，阈值按语言分——一个中文字符在搜索结果里的宽度大约是拉丁字符的两倍，用一个绝对字数对两种语言都是错的
- hreflang 集合是否包含自己（自引用是必需的）、是否恰好一个 `x-default`、tag 是否合法 BCP 47
- 每个 JSON-LD 块能否解析、`@type` 是否存在、`@id` 是否重复、是否有未转义的 `</script`
- 每个 `<img>` 是否有 alt，是否有宽高（没有宽高就是直接的 CLS 损失）
- 外链是否带 `rel=noopener`

**跨页面：**

- hreflang 是否互指。A 指向 B 而 B 不指回 A 时，Google 会丢掉整个语言簇，所以这一项是失败而不是警告。
- 每个内链是否真的对应 `dist/` 里存在的文件
- 每个可索引页面是否在 sitemap 里；每个 noindex 页面是否**不**在 sitemap 里

## 它抓到过的东西

写它的直接回报是抓到了两个我自己不会发现的 bug。

一个是中文标签页的语言切换链接指向 `/en/blog/tags/工程实践`——一个从中文标签生成的 slug 在英文站没有对应页面。在浏览器里点一下才会发现，而我不会去点每一个标签。

另一个是几个英文页面的 meta description 只有 10 到 19 个字符。全都是从 UI 字符串继承来的，本地看起来没问题，在搜索结果里就是一行没信息量的字。
