fix(seo): prevent multi-h1 + merge leetcode duplicates#342
Merged
Conversation
Bing Webmaster 报 4 个页面有多个 <h1>。仓库 324 个 MDX 里 181 个正文
都写了 # 一级标题(markdown 习惯),叠加 page.tsx 渲染的 <h1>{title}</h1>
形成双 h1,是 SEO/无障碍反模式。
不能要求贡献者改写习惯,所以在 build 阶段加 remark 插件自动处理:
function remarkShiftHeadingIfH1(tree) {
if (tree 含 h1) {
所有 heading.depth += 1 (h1→h2 / h2→h3 / ... / h5→h6)
}
}
效果:
- 贡献者照常写 # 标题 / ## 章节 / ### 子节
- 渲染变 <h2> / <h3> / <h4>,保持层级关系
- page.tsx 的 <h1>{title}</h1> 是页面唯一 h1
- MDX 不含 h1 时不动 (作者已从 ## 起,天然合规)
本地验证:
- bq.html: 之前 2 h1 → 1 h1 ✅
- cs/index.html: 之前 3 h1 (含两个 # 中文混排) → 1 h1 ✅
- leetcode 抽样: 1 h1 ✅
历史背景:page.tsx 渲染 h1 是 2025-09-19 commit 7b270d5 加的,
社区贡献的 mdx 早就在写 # 一级标题,双 h1 隐患存在 8 个月直到 Bing
2026-05 扫描才报出。
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Contributor
There was a problem hiding this comment.
Pull request overview
This PR addresses an SEO/accessibility issue where docs pages can render multiple <h1> tags by automatically shifting MDX heading levels at build time when an h1 is present, ensuring the page-level <h1>{frontmatter.title}</h1> remains the only H1.
Changes:
- Adds a new remark plugin to shift all headings down by one level when an MDX document contains an
h1. - Wires the new plugin into the global MDX
remarkPluginspipeline.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+61
to
+63
| const hasH1 = tree.children.some( | ||
| (n) => n.type === "heading" && n.depth === 1, | ||
| ); |
Bing 报 89 个重复 title。逐组分析后:
A) 8 组 leetcode 同题多文件版本("维护漂移"产物,命名规范迭代时没清理):
5 组 zh 有双版本,docId 完全相同,是同一笔记不同阶段:
- xxx.md(早期命名,含中文/特殊字符,部分 title 还有 ".md" 后缀 bug)
- xxx-slug.zh.md(slug 规范化版本)
→ 删除 .md + 对应 .en.md(10 个文件),保留规范化版本。涉及题号:
2270 / 93 / 2241 / 46 / 3138
3 组 zh 是孤立中文版(仓库里早就有对应的 slug 化 .en.md 但没配对 .zh.md):
- 42.md / 2131. xxx.md / [1545]xxx.md
→ 重命名为 slug 化(建立 zh+en hreflang 配对),删 translator 这次生成的
.en.md(保留早就存在的 slug 化 .en.md)
顺手修复 42 / 2131 的 title 残留 ".md" 后缀(之前 bug)
B) 2 组跨分区同名 "参考资料" / "References"(probability-statistics/resources
和 linear-algebra/resources 都用通用 title):
→ 改成"概率论与统计学参考资料" / "Probability & Statistics References"
和"线性代数参考资料" / "Linear Algebra References"
效果:
重复 title 组数:30 → 20(剩 20 组是 zh/en 同字面,论文/项目名通用
英文专有名词,有 hreflang 配对,Google/Bing 一般不视为重复)
注:删除的文件可以从 git 历史恢复(保留 docId 一致追溯)。
Copilot CR: `tree.children.some(...)` 只看顶级子节点,h1 嵌套在 blockquote / list 里(markdown 允许 \`> # title\`)会漏判。 改用 visit 整树扫描检测 h1。代价是多一次 traversal(约几十微秒每文档), 确保所有位置的 h1 都被识别并触发整树 demote。
longsizhuo
pushed a commit
that referenced
this pull request
May 12, 2026
用户 review 后指出"丢西瓜捡芝麻"风险,复盘 Vercel 30 天 dashboard 后修订: ## 真实根因(不是 /_not-found) dashboard 30 天曲线显示 5/11 CPU 峰值 80-90min(基线 5-15min/day),完美 对应 SEO PR 落地时间: - 5/11 15:01-15:39 UTC: PR #341 (253 MDX descriptions + 32 新 EN 翻译) - 5/11 16:02-18:41 UTC: PR #342 (remark heading shift + leetcode dedup) - 5/11 19:01-19:27 UTC: PR #343 + #340,4 小时 4 次 deploy 清空 ISR 加上 deploy.yml 里 IndexNow 主动告诉 Bing 重抓 → 5/10-5/12 crawler 风暴。 **这是 SEO 工作 successful 的代价,不是 bug。** 真实流量。 /_not-found 静态化 + bot blocklist 是真实 waste 清理(保留),但不能独立 解释 4× 激增。 ## 撤回的两条 hack 1. Sentry tracesSampleRate 0.1 → 0.02:撤回,保持 10% observability 不能为这点 CPU 让步,10% 是行业标准,client/server/edge 三处必须一致才能跨 runtime 串联 trace。 2. fetchEvents 失败一律返空:改成只在 NEXT_PHASE === phase-production-build 时返空,运行时仍 throw 让 Sentry 抓真故障。否则 prod backend 挂了会被 误显示成"暂无活动",掩盖故障。 ## 保留的修复(best practice,不是 hack) - /_not-found ƒ → ○:根 404 本就不需要 i18n - proxy.ts bot blocklist:扫描器不该烧 Fluid - /[locale]/docs /events /login 缺 setRequestLocale → 补:SSG/ISR 本就该工作 - /editor /share cascade ●:纯 client component,安全 ## Build 验证 pnpm build 重跑: - /[locale]/events 仍是 ● ISR 5m 1y - [events] fetch failed at build, rendering empty shell(NEXT_PHASE guard 工作)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Bing Webmaster 报 4 个页面有多个 `
` 标签(High severity)。仓库 324 个 MDX 中 181 个 正文写了 `# 一级标题`,叠加 `page.tsx` 渲染的 `
{frontmatter.title}
` 形成双 h1,是 SEO/无障碍反模式。Root cause
Fix
不能要求贡献者改写 markdown 习惯,所以在 build 阶段加 remark 插件自动 shift:
```ts
function remarkShiftHeadingIfH1(tree) {
if (tree 含 h1) {
所有 heading.depth += 1 (h1→h2 / h2→h3 / ... / h5→h6)
}
}
```
` / `
` / `
`,保持层级关系
{title}` 是页面唯一 h1
验证(本地 prerendered HTML)
`pnpm build` / `pnpm typecheck` / `pnpm test` 全过。
副作用
mdx 用到 h5 时被降到 h6;h6 已是 markdown 最大深度无法再降,保留为 h6。CS/AI 技术文档基本不到 5 层,影响极小。
Test plan