Google 在 SERP 显示的标题不是 <title> 标签里写的——而是页面 H1、第一段文字、站名拼接、或它觉得更匹配 query 的某段话。Google 官方文档说约 60% 的 SERP 标题用了你写的 <title>,剩下 40% 它会改写。
要把”被改写率”从 40% 推回 15% 以下,需要逐条修以下几个触发点。
常见原因
1. <title> 太长
桌面端 SERP 视觉宽度约 580px,对应英文 50-60 字符、中文 28-32 字。超长会被截断,Google 倾向直接改写而不是显示半句。
如何判断:
curl -sL https://yourdomain.com/page | grep -oE '<title>[^<]+</title>'
# 数字符或汉字数
2. 关键词堆砌 / 用竖线拼
"AI 工具 | ChatGPT | Claude | 最佳 2026 | 程序员必备" 这种结构 Google 直接识别为 SEO 优化,改写成 H1 + 站名。
3. <title> 与页面意图不匹配
<title> 写”完整指南”但正文只有 300 字,Google 会从正文抽一句更贴的话顶上。或者标题里的关键词在正文里根本没出现。
4. 页面没 H1 或多个 H1
Google 重度依赖 H1 作为标题信号。0 个 H1 / 多个 H1 → 它从其他地方拼一个标题,常常用站名 + 第一段。
如何判断:
curl -sL https://yourdomain.com/page | grep -oE '<h1[^>]*>[^<]+</h1>'
# 应该正好 1 行
5. <title> 与 H1 内容相差太大
如果两者表达的是完全不同的意思,Google 会信 H1 不信 title。
6. 多页用了完全一样的 <title> 模板
"博客 - 公司名" 全站重复 → Google 必须用其他信号区分,会自己改写。
最短修复路径
Step 1:找出哪些 URL 被改写了
Search Console → 性能 → “页面”按展示量降序。挑 Top 20 URL,对每个:
# 看你的 <title>
curl -sL "https://yourdomain.com/url" | grep -oE '<title>[^<]+</title>'
# 在 Google 搜
site:yourdomain.com/url
把 SERP 显示的标题和源码 title 对比,不同的就是被改写。
Step 2:按模板重写 title
[用户意图词或主关键词] + [独有限定/数字/年份] + [品牌(可选,放后面)]
例子(差 → 好):
| 差 | 好 |
|---|---|
AI 工具 | ChatGPT | Claude | 最佳 2026 | 2026 我真正付费的 12 个 AI 工具:按每周省时排序 |
博客 - MyCompany | Astro 部署 Vercel 完整指南(2026 更新) |
完整指南(无主题) | hreflang 配置完整指南:6 个常见错误 + 修复路径 |
字符预算:
- 英文:50-60 字符
- 中文:26-30 字(含标点)
主关键词放前 30 个字符内,移动端截断后还能看到。
Step 3:保证页面有且只有一个 H1
# 全站扫
for f in $(find dist -name "*.html"); do
count=$(grep -oE '<h1[\s>]' "$f" | wc -l)
[ "$count" != "1" ] && echo "$f: H1=$count"
done
修:
- 模板里的
<header><h1>站名</h1></header>改成<header>站名(无 h1)</header> - 文章正文一定有 1 个
<h1>[文章标题]</h1> <h1>和<title>表达同一个意思(不用完全一字不差,但语义对齐)
Step 4:title 模板每页独有
如果你用 CMS / 静态站生成器,模板里:
---
const { title, brand = "MyCompany" } = Astro.props;
const fullTitle = `${title} | ${brand}`;
// 限长:超过 60 字符就只用 title
const safeTitle = fullTitle.length <= 60 ? fullTitle : title;
---
<title>{safeTitle}</title>
Step 5:用 build 阶段拦截重复
// scripts/check-titles.mjs
import fg from "fast-glob";
import fs from "node:fs";
const seen = new Map();
const issues = [];
for (const f of fg.sync("dist/**/*.html")) {
const html = fs.readFileSync(f, "utf8");
const title = html.match(/<title>([^<]+)<\/title>/)?.[1]?.trim();
if (!title) issues.push(`MISSING: ${f}`);
else if (title.length > 60) issues.push(`TOO LONG (${title.length}): ${f}`);
else if (seen.has(title)) issues.push(`DUPLICATE: ${f} == ${seen.get(title)}`);
else seen.set(title, f);
}
if (issues.length) { console.error(issues.join("\n")); process.exit(1); }
Step 6:修完等 14 天看 CTR
Search Console → 性能 → 按 URL 对比改动前后的 CTR。如果从 1.5% 涨到 3% 以上,说明修对了。
预防建议
- title 像人话,不是机器人——读出来不卡顿
- 每页有且仅有一个清晰 H1,与 title 语义对齐
- title 模板永远
{独有内容} | {品牌},独有内容放前面 - 60 字符是硬上限,超过自动截品牌段
- CI 加 build 阶段拦截:重复 title / 缺失 title / 过长 title 都报错