先从SEO元数据说起。很多站长以为在页面头部塞个title和description标签就够了,但搜索引擎真正关注的是什么?是你服务端返回的那份初始HTML源码里,这些元信息是否唯一、精准且与页面内容高度匹配。如果采用硬编码或依赖客户端二次修改,很遗憾,这条路走不通——Google不会给你任何通融,降权处理、搜索结果截断展示都是常见后果。
先给出一个核心结论:SEO元数据必须在服务端预构建阶段动态生成并注入到HTML中。在Next.js框架中,这一过程发生在getStaticProps或getServerSideProps里;VuePress这类静态站点工具则借助frontmatter原生实现;而PHP静态站点需要前置声明变量并做转义输出。无论你使用哪种技术栈,核心逻辑始终一致——元数据必须在服务端确定,绝不能在客户端运行时临时拼接。

在服务端预构建(SSG/SSR)阶段,title和meta name="description"必须根据页面上下文动态计算并注入。如果采用硬编码或设置全局降级方案,最终会导致全站标题雷同、描述信息缺失,Google搜索引擎会直接降权或截断展示。这绝非危言耸听,而是实际运营中反复验证的教训。
Next.js 中 getStaticProps 里生成唯一 title 和 description
静态生成阶段无法访问URL参数以外的运行时状态,因此元数据必须在getStaticProps或getServerSideProps中完成计算。切记不要在组件里通过useEffect来二次修改——那只能改变客户端DOM,搜索引擎抓取的是服务端返回的初始HTML源码。
- 务必将内容数据(如文章标题、摘要)和元信息逻辑写在
getStaticProps返回的props对象中,示例:props.pageTitle = data.title + " | 官方文档" title标签的字符数建议控制在50–60个字符,核心关键词尽量前置;description描述标签控制在120–155字符,开头包含主关键词,使用自然通顺的语句,避免堆砌关键词- 避免在
getStaticProps内部调用未序列化的函数(如new Date().toISOString()),否则会导致构建失败;时间类字段应提前转为字符串格式再传入
VuePress / VitePress 的 frontmatter 是最轻量的动态元数据源
这类静态站点工具直接将YAML格式的frontmatter解析为页面上下文,title和description可以原生映射到和标签,几乎不需要额外配置。
- 在每个
.md文件顶部声明元数据:---title: HTML SEO 元数据实战description: 详解如何在静态站点中正确生成可被 Google 抓取的 title 与 description 标签--- - 不要在frontmatter里写JavaScript表达式(例如
title: ${siteName} - ${pageName}),这类写法不会被解析,只会当作普通字符串原样输出 - 如果需要跨页面复用描述模板,应通过主题插件或自定义
enhanceApp注入逻辑来实现,而不是在frontmatter里做字符串拼接
PHP 静态化站点中变量前置声明是唯一可靠方式
没有数据库支撑的PHP静态站点无法使用路由中间件,只能依靠代码执行顺序来确保变量在include head.php之前已经存在。任何"先引入头部再赋值"的写法都会导致$page_title变量为空或undefined。
- 页面第一行代码必须是变量声明:
- 在
inc/head.php文件中直接输出:,注意务必使用= htmlspecialchars($page_title) ?> htmlspecialchars函数防止XSS攻击 - 不要使用
define()常量或global全局变量,作用域混乱且容易被覆盖;也不要依赖$_SERVER['REQUEST_URI']手动解析路径来生成title——URL规则一旦变化,全站元数据就会崩溃
真正关键的问题不在于"怎么写",而在于"谁来负责写":SEO元数据永远不应该由前端组件在运行时拼接,也不能依赖JavaScript注入来事后补救;它必须是服务端输出HTML时就已经确定且不可变的一段字符串。如果忽略了这个前提条件,所有动态优化都只是表面功夫,无法真正生效。
