服务端预构建时元数据必须动态生成,不能依赖客户端JS;Next.js需用getStaticProps+metadata对象或generateMetadata函数,Astro用frontmatter,且canonical须为绝对URL、description须用mb_strimwidth按字符截断。

服务端预构建时元数据必须动态生成,不能靠客户端JS补
搜索引擎爬虫不执行 JavaScript,只解析 HTML 原始文本。你在 Angular 里用 metaService.addTag()、Vue 里用 document.title = ... 或 React Helmet 动态改 <title>,对 SEO 完全无效——产出的 HTML 文件里还是默认值或空内容。
常见错误现象:
• Google 搜索结果中多个页面标题一模一样
• “站点:yourdomain.com” 只收录首页,其他页面不进索引
• 百度提示“描述重复率过高”
- Next.js 必须在
getStaticProps中返回页面专属数据,并通过export const metadata = { title, description }注入(13.4+ 推荐) - Astro 页面直接在 frontmatter 写
title:和description:,构建器自动提取到<head> - 纯 PHP 预构建场景下,禁止写死
<title>My Blog</title>,应从 Markdown front matter 或 JSON 元数据文件读取并转义输出
description 截断必须用 mb_strimwidth,不是 substr
中文、Emoji、全角标点在 UTF-8 下占多字节,用 substr($text, 0, 150) 极易切开字符导致乱码,甚至破坏 HTML 结构(比如把 < 切成半截变成 < 后续被浏览器误解析)。
正确做法是用 mb_strimwidth($text, 0, 150, '', 'UTF-8'),它按字符而非字节截断,末尾自动省略号,且不破坏编码。
立即学习“前端免费学习笔记(深入)”;
- 长度控制在 150–160 字符内,含核心关键词 2–3 次
- 避免模板兜底:不要设全局 fallback 描述,否则所有页面共享同一段文字
- 若从正文提取摘要,优先取前几段非空文本,跳过广告、侧栏等干扰内容
canonical 标签必须用绝对 URL,且按页面路径逻辑推导
<link rel="canonical"> 不是装饰,是告诉搜索引擎“这个页面的唯一权威地址”。写错会直接导致重复内容降权。
容易踩的坑:
• 写相对路径如 /blog/123 → 部署在子路径时解析错误
• 所有页面都硬编码同一个 canonical → 爬虫认为全是副本
• 参数型 URL(如 /blog?id=123)没映射到 clean path
- 构建时根据当前页面路径规则生成 canonical:比如将
/blog?id=123映射为https://example.com/blog/123 - URL 必须带协议和域名,即绝对 URL
- Next.js 的
metadata.canonical、Astro 的canonicalfrontmatter 字段都支持动态计算
OG/Twitter 卡片元数据必须与页面内容严格一致
社交平台抓取时只看初始 HTML,不会等 JS 渲染。如果 og:title 是静态模板值,而实际页面标题是 JS 加载后才设置的,分享出去就显示错内容。
典型问题:
• 分享文章页,卡片显示“加载中…”或首页标题
• og:image 404 或指向占位图
• og:url 指向错误路径,导致点击后跳转异常
- 所有
og:*和twitter:*标签必须和服务端生成的<title>、<meta name="description">同源、同逻辑生成 - 图片 URL 必须可公开访问、尺寸合规(至少 1200×630px),且不能是本地路径或未部署资源
- 避免在模板里写
<meta property="og:title" content="{{ $title }}">这类裸变量,统一走封装函数做空值校验和转义



















