news 2026/10/2 12:38:01

成为全栈·Next.js 网站前台篇·Next.js SEO:metadata、结构化数据、sitemap 与 robots

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
成为全栈·Next.js 网站前台篇·Next.js SEO:metadata、结构化数据、sitemap 与 robots

成为全栈·Next.js 网站前台篇·Next.js SEO:metadata、结构化数据、sitemap 与 robots

SEO 不是给页面塞一组关键词。搜索引擎需要稳定的公开 URL、与正文一致的元数据、可安全解析的结构化数据,以及一张不包含私有页面的站点地图。

前言

内容站使用 Next.js,大家很容易说出一个理由:对 SEO 友好。但“服务端能生成 HTML”只是起点。标题可能与正文不一致,canonical 可能指向本机,JSON-LD 可能形成脚本注入,sitemap 可能只列出前 100 篇文章,会员中心也可能被错误加入索引。

当前项目把 SEO 拆成四个相互校验的出口:页面 metadata 描述当前内容,JSON-LD 给出机器可读实体,sitemap 提供公开 URL 清单,robots 表达爬虫访问策略。它们必须共享同一套 URL 和公开范围。

先明确四种机制分别解决什么

机制主要回答不能替代什么
metadata当前页面标题、描述、分享卡片是什么不能保证页面可抓取
JSON-LD页面中的文章实体有哪些结构字段不能代替可见正文
sitemap.xml本站希望搜索引擎发现哪些公开 URL不能强制收录
robots.txt哪些路径允许或不建议抓取不能作为权限控制

robots 中写了Disallow,并不意味着页面有安全保护;任何私有数据仍必须由后端鉴权。反过来,URL 出现在 sitemap 中也不等于一定获得排名。

动态 metadata 应与正文复用同一篇文章

详情页用 Reactcache()包住文章读取,generateMetadata与页面正文消费同一份请求结果:

const load = cache(async (slug: string) => { return getArticle(slug).catch((error) => { if (error instanceof ApiError && error.status === 404) notFound() throw error }) }) export async function generateMetadata({ params }: Props): Promise<Metadata> { const article = await load((await params).slug) return { title: article.title, description: article.summary || undefined, alternates: { canonical: articleUrl(article) }, } }

如果 metadata 单独调用另一套接口,就可能在缓存再验证窗口里出现标题版本 A、正文版本 B。请求内复用不能解决所有跨时刻缓存问题,但至少消除了同一次渲染中的重复读取。

Open Graph 需要真实字段,缺失就省略

return { title: article.title, description: article.summary || undefined, openGraph: { title: article.title, description: article.summary || undefined, type: 'article', publishedTime: article.publishedAt || undefined, images: article.coverImage ? [article.coverImage] : undefined, }, }

没有封面时不应该填一条不存在的图片地址;没有摘要时,也不应截断 Markdown 源码塞进 description。默认站点图和默认描述可以在根 layout 统一配置,文章页只覆盖确实存在的内容字段。

canonical 则必须使用正式站点 origin。若部署时仍保留http://127.0.0.1:13001,搜索引擎看到的规范地址就会错误。因此NEXT_PUBLIC_SITE_URL是上线配置的一部分,不是一项可忽略的本地便利变量。

所有文章链接统一经过articleUrl

exportconstarticleUrl=(article:{id:number;slug?:string|null}):string=>`/articles/${encodeURIComponent(article.slug||String(article.id))}`

页面链接、canonical、sitemap 和相关文章都调用同一函数。slug 存在时使用 slug,历史数据没有 slug 时回退 id。若四个出口分别拼接 URL,一次路由策略调整就可能制造重复地址。

更严格的系统还会将 id 旧地址 301 重定向到 slug 地址。当前项目允许两种标识被 API 读取,但公开链接统一优先 slug,减少主动产生重复 URL。

JSON-LD 不是普通字符串拼接

文章页输出BlogPosting:

<script type="application/ld+json" dangerouslySetInnerHTML={{ __html: jsonLd({ '@context': 'https://schema.org', '@type': 'BlogPosting', headline: article.title, datePublished: article.publishedAt || undefined, dateModified: article.updatedAt, author: { '@type': 'Person', name: article.authorName || '作者', }, description: article.summary || undefined, }), }} />

危险点在于</script>可以提前闭合脚本标签。项目在序列化后转义小于号:

exportconstjsonLd=(value:unknown):string=>JSON.stringify(value).replace(/</g,'\\u003c')

不要手写 JSON 字符串,也不要因为 MIME 类型是application/ld+json就认为插值天然安全。安全边界仍然是正确序列化与脚本闭合字符处理。

sitemap 必须遍历完整分页

只请求pageSize=100并不等于站点最多只有 100 篇文章。当前实现按接口返回的总页数继续读取:

letpage=1lettotalPages=1do{constdata=awaitlistArticles({page,pageSize:100})for(constarticleofdata.list){entries.push({url:newURL(articleUrl(article),origin).href,...(article.updatedAt?{lastModified:article.updatedAt}:{}),})}totalPages=data.pagination.totalPages page+=1}while(page<=totalPages)

分类、标签和实际出现过的作者主页也被加入:

for(constcategoryofflattenCategories(tree)){if(category.slug)entries.push({url:newURL(`/categories/${category.slug}`,origin).href})}for(consttagofallTags){entries.push({url:newURL(`/tags/${tag.slug}`,origin).href})}

搜索结果、登录注册、会员中心和 API 不进入 sitemap。内容量明显增长后,还要评估生成时间、URL 去重和 sitemap 分片;当前全量遍历适合现阶段规模,不是无限扩展方案。

robots 表达抓取策略,不承担鉴权

constrobots=():MetadataRoute.Robots=>({rules:{userAgent:'*',allow:'/',disallow:['/member/','/api/','/login','/register','/search'],},sitemap:newURL('/sitemap.xml',SITE_URL).href,})

搜索页被排除,是因为关键词组合可能无限扩张,容易制造低价值重复页面。会员 layout 还会输出noindex, nofollowmetadata:

export const metadata = { title: '会员中心', robots: { index: false, follow: false }, }

两层表达可以减少误抓取,但真正的私有内容保护仍在 API 权限校验。知道 URL 的任何人都能忽略 robots.txt 发请求。

让四个出口互相核对

检查对象应满足的不变量
页面 canonical与站内文章链接采用相同 slug 策略
Open Graph URL/图片使用可公开访问的绝对地址
JSON-LD headline与可见的<h1>一致
sitemap 文章 URL不漏分页,不含草稿和会员页
robots sitemap 地址使用正式站点 origin
私有页面不进 sitemap,同时带 noindex,且后端鉴权

生产验收不能只看页面源代码

pnpmbuildpnpmstartcurl-shttp://localhost:3000/sitemap.xmlcurl-shttp://localhost:3000/robots.txtcurl-shttp://localhost:3000/articles/example-slug

至少要核对:详情页 title、description、canonical 和 JSON-LD 是否对应同一文章;第二页以后的文章是否出现在 sitemap;会员路径是否被排除;正式构建是否注入正确站点域名;包含</script>测试文本的标题或摘要不会提前闭合 JSON-LD。

本地通过仍不能证明搜索引擎已经抓取或收录。上线后还要通过搜索平台观察抓取错误、规范地址选择和结构化数据报告,这些属于生产证据。

适用边界

小型站点使用一个动态 sitemap 足够。达到数万或数十万 URL 后,应使用 sitemap index 分片,并避免每次生成都串行扫描全部内容。

结构化数据字段也不应为了“更丰富”而虚构。没有图片、出版组织或修改时间时,可以省略;错误字段比少字段更容易让机器理解与真实页面分叉。

小结

Next.js 提供了 metadata、sitemap 和 robots 的文件约定,却不会自动保证 SEO 正确。真正的质量来自同一套公开 URL、同一份文章事实和明确的公开范围。

当 canonical、站内链接、JSON-LD 与 sitemap 指向同一篇文章,会员与搜索页又被清楚排除,SEO 才从几段配置变成了可以验证的系统契约。

延伸阅读

  • 文章阅读辅助
  • 数据获取与缓存
  • 站点设置如何驱动页头、页脚与 SEO

如果这篇文章对你有帮助,欢迎订阅我的 CSDN 专栏「成为全栈」:

🔗 专栏地址:https://blog.csdn.net/fungleo/category_13204651.html

📦 本系列配套代码仓库:https://github.com/fengcms/become-a-full-stack-developer

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/2 12:38:01

DAY1 HTML1-29

1.<起始标签>文本&#xff08;标签体&#xff09;</结束标签> 单标签&#xff1a;<input/>&#xff08;/可省略&#xff09; 2.嵌套&#xff1a; <起始标签>标签体&#xff08;前面空四格/tab键&#xff09; <input>&#xff08;前面空四格/t…

作者头像 李华
网站建设 2026/10/2 12:36:38

国庆假期自学GEO从哪下手?四步顺序别搞反了

自学最大的坑不是"学不会"&#xff0c;是"不知道从哪开始"。GEO涉及的内容很多——选题、写作、发布、验证——如果没有清晰的顺序&#xff0c;很容易东一榔头西一棒子&#xff0c;学了一个月还在原地。自学GEO的正确起步顺序不要同时学&#xff0c;要按顺…

作者头像 李华
网站建设 2026/10/2 12:35:30

从网络层阻断到DNS过滤:恶意域名C2回连事件完整处置复盘

从事网络安全这些年&#xff0c;我对网络层&#xff08;Internet Layer&#xff09;的认识一直在变。刚入行时觉得它就是IP分组的转发逻辑、路由协议的堆叠&#xff0c;直到真正跟黑产团伙正面对抗&#xff0c;才意识到网络层才是最容易被忽略、也最该优先利用的防御阵地。前阵…

作者头像 李华
网站建设 2026/10/2 12:34:57

向量数据库怎么选?Milvus、Qdrant、pgvector 横向实测

做 RAG 或语义检索&#xff0c;绕不开向量数据库这道选择题。市面上候选一堆&#xff0c;纠结的根源是需求差异大。这篇按真实使用体验&#xff0c;把三个主流候选放到同一把尺子上量一量。 先想清楚四个问题 选型前先回答&#xff1a;数据量多大&#xff1f;检索延迟要求多严&…

作者头像 李华
网站建设 2026/10/2 12:33:02

广州肩带定制源头厂家行业观察与实务选择参考:广受信赖的生产厂家靠谱商家测评排名

汕头市布兰婷服饰有限公司品牌摘要汕头市布兰婷服饰有限公司是一家专注文胸、泳衣内衣辅料研发、生产与销售的现代化多元化企业&#xff0c;深耕内衣辅料赛道&#xff0c;主打各类文胸辅料及配套配件系列产品&#xff0c;能够为内衣生产及销售全链条提供一站式配套解决方案。企…

作者头像 李华
网站建设 2026/10/2 12:32:43

国产化环境文件上传下载三大方案与踩坑实践

开局先聊点实际的&#xff1a;国产化终端上&#xff0c;最容易翻车的不是业务逻辑&#xff0c;而是文件上传下载。很多团队在移植Web系统时&#xff0c;功能测试都过了&#xff0c;一放到国产化环境就直接卡壳——要么传不上去&#xff0c;要么下载下来是乱码&#xff0c;要么浏…

作者头像 李华