关于 optimise-seo
optimise-seo 是面向 Next.js App Router 项目的技术 SEO 工作流技能,指导开发者处理抓取、元数据、结构化数据、性能和验证,不涉及视觉设计。
主要功能
- 01路由盘点与索引策略
- 02站点地图、robots、规范链接与重定向
- 03元数据与结构化数据
- 04语义 HTML 与站内链接
- 05Core Web Vitals 与技术加固
- 06基于页面响应的验证
适用场景
- 01排查抓取和索引问题
- 02为 Next.js 页面添加元数据与 JSON-LD
- 03处理重定向和多语言索引
- 04依据页面响应检查 SEO 改动
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ mblode/ agent-skills --skill optimise-seo安装后在 Claude Code 中输入/optimise-seo确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
evals/evals.jsonreferences/answer-engines.mdindexing-policy.mdinternationalisation.mdnextjs-implementation.mdseo-checklist.mdtechnical-hardening.mdvalidation-evidence.md详细介绍
优化 SEO
- **包含:**在 Next.js App Router 代码库中实现可抓取性、元数据、结构化数据、规范链接、重定向、状态码、hreflang、AI 爬虫策略和可提取的页面结构、Core Web Vitals、程序化 SEO、安全与隐私标头,以及错误页面行为。
- **不包含:**视觉改版、布局或页面级 UI 质量(
ui-design的 Direction、Build 和 Audit 模式)、需求研究、简报、问题地图或 Search Console 监控(seo-program)、针对robots.txt和sitemap.xml的租户路由(multi-tenant-architecture),以及撰写文章(外部ghostwriter)。
允许修改的范围:metadata 和 generateMetadata、JSON-LD、语义化 HTML、内部链接、alt 文本、app/sitemap.ts、app/robots.ts、app/feed.xml/route.ts、app/llms.txt/route.ts、proxy.ts、next.config.ts 中的重定向和标头、错误页面以及性能优化。组件样式和布局保持不变;如果修复需要更改布局,应将其列为交接给 ui-design 的事项。
编写代码前,请阅读 node_modules/next/dist/docs/ 中对应的指南(从应用目录解析;在 monorepo 中,无法从仓库根目录看到 next 包)。捆绑的文档与已安装的版本相符;references/nextjs-implementation.md 中的说明已根据 16.3 核查,并指出了行为变更之处。
参考资料
| 文件 | 阅读时机 |
|---|---|
| references/indexing-policy.md | 涉及重定向、重复 URL、索引决策或大规模生成页面时 |
| references/nextjs-implementation.md | 步骤 2-4 编写代码前:元数据、站点地图和 robots、重定向、索引、流式 404、JSON-LD、OG 图片、CSP |
| references/answer-engines.md | 任务涉及 AI 爬虫、针对 GPTBot 或 ClaudeBot 的 robots.ts 规则、llms.txt、Content-Signal 或“为什么没有引用我们”时 |
| references/internationalisation.md | 网站包含多种语言区域时 |
| references/technical-hardening.md | 涉及标头、Cookie、同意、GPC、security.txt,或维护与错误状态任务时 |
| references/validation-evidence.md | 步骤 5:已提供页面的命令和证据要求 |
| references/seo-checklist.md | 步骤 5:复制到报告中,并为每一项标注证据 |
工作流
复制并跟踪此检查清单:
SEO progress:
- [ ] Step 1: Inventory routes and decide index intent per route
- [ ] Step 2: Fix crawl/index foundations (sitemap, robots, canonicals, redirects, status codes)
- [ ] Step 3: Implement metadata and structured data
- [ ] Step 4: Improve semantics, extractable answers, internal links, and Core Web Vitals
- [ ] Step 5: Validate with references/seo-checklist.md and report evidence
步骤 1 是一张表格:路由、是否索引、规范链接、原因。后续每个步骤都以此为依据;没有对应行的路由不做任何处理。
每个站点都必须具备
app/sitemap.ts列出每个可编入索引的 URL,lastModified根据内容生成;app/robots.ts声明该站点地图- 每个页面只设置一个规范 URL,统一主机名、大小写和尾部斜杠策略,并在根布局中通过
alternates.canonical和metadataBase设置。Next.js 会自行强制执行斜杠策略:默认将/about/以 308 重定向到/about,设置trailingSlash: true则反过来(带扩展名的文件和.well-known/路径在两种情况下都不受影响) - 保留该属性现有的所有权验证。仅当提供了令牌且选定了验证方式时才使用
metadata.verification;DNS 验证无需重复添加 meta 标签 - 在根布局的
robots中解除 Google 的默认预览上限:googleBot: { 'max-snippet': -1, 'max-image-preview': 'large', 'max-video-preview': -1 }。不设置这些值,Google 就会截断摘要和图片预览;AI 界面读取摘要上限,以判断可以引用页面的多少内容 - 在根布局中显式设置
authors和creator,这样个人级别的署名就不会只出现在页脚 HTML 和 JSON-LD 中 - 每个页面都有独特的标题和描述;标题和 H1 以非品牌主要关键词开头,且意图一致(分类页面使用以品牌为主的标题,只会竞争网站已经占据优势的导航类查询)
- 每个页面开头都用简短的纯文本回答该页面旨在回答的问题;h2 标题应是用户实际提出的问题
- 设置 Open Graph 和 Twitter Card 标签,并使用 1200x630 图片
- JSON-LD:在根
@graph中只定义一次 Organization 和 WebSite,并为其设置稳定的@id;内页使用 BreadcrumbList;然后根据内容类型使用 Article、Product、ProfilePage 或 LocalBusiness。实体只定义一次,并通过@id引用 - 每个页面只有一个 h1;h2-h6 层级合理;替代文本具有描述性;相关页面之间设置内链
- Core Web Vitals 达到 CrUX 实测数据 p75 的目标值:LCP 2.5 s、INP 200 ms、CLS 0.1(INP 于 2024 年 3 月取代 FID;仍提及 FID 的指南已经过时)
重定向和索引策略
当路由变更、需要合并重复内容,或页面索引策略发生变化时,加载 references/indexing-policy.md。根据已安装的 Next.js 版本和实际响应验证行为。
AI 爬虫和答案引擎
Google 无需特殊文件:AI Overviews 和 AI Mode 使用 Googlebot,并依赖核心排名机制;唯一的退出方式是设置 nosnippet 或 max-snippet:0(这也会移除常规摘要)。其他情况都取决于针对不同爬虫类别的 robots 决策:搜索和引用机器人(OAI-SearchBot、Claude-SearchBot、PerplexityBot)会让网站出现在答案中;训练机器人(GPTBot、ClaudeBot、CCBot)不会;Google-Extended 和 Applebot-Extended 是使用令牌,背后没有对应的爬虫。llms.txt 只增加一个路由,几乎没有可衡量的收益。完整表格、robots.ts 规则、Content-Signal 和 Markdown 替代模式见 references/answer-engines.md。
程序化 SEO
对于批量生成的页面,加载 references/indexing-policy.md。为新模式建立索引前,必须有需求证据,且页面具有独特价值。
审计问题排查顺序
- 抓取与索引:robots、sitemap、多余的
noindex、规范链接、重定向链、软 404 - 技术:HTTPS、Core Web Vitals 实测数据、移动端与桌面端的一致性、通过 CDN 访问 AI 爬虫
- 页面内:title 和 H1 的唯一性及非品牌词开头、可提取的答案、内部链接、内容单薄的页面
- 孤立页面:可编入索引但没有内部链接或 sitemap 条目的页面;为其提供抓取路径,或添加
noindex并移除。对于权威性较低的域名,全站页脚链接并不算抓取路径:Google 会降低页脚模板内容的权重,而仅通过页脚链接的版块可能数月都显示“Google 不知道该 URL”,与此同时,旁边通过导航链接的页面却每天都被抓取
按相同顺序报告:先说明阻碍索引的问题,再说明页面内和 schema 的缺漏,最后给出唯一的下一步修复措施。列出 20 个权重相同的问题,结果就是一个也解决不了。报告长度应取决于发现的问题,而非章节列表。第三方审计工具的结果应视为线索,而非已确认的问题:有些工具会错误地标记 Next.js 输出(例如 <title> 已存在,却称缺少 <meta name="title">;把明暗主题变体的 theme-color 标签称为“重复”;或指出重复的 article:tag),而且至少有一个 agent-skill 审计工具在未提供明确模块列表时会静默跳过模块,还会放过答案从未渲染的 FAQPage。将每条线索与实际提供的 HTML 核实后,再写入报告。
注意事项
- 在
cacheComponents: true(scaffold-nextjs的默认值)下,dynamic、dynamicParams、revalidate和fetchCache分段配置会导致构建错误:例如在app/sitemap.ts中使用export const revalidate、在robots.txt/route.ts处理程序中设置dynamic = 'force-static',以及在任何位置使用fetchCache(runtime、maxDuration、instant和prefetch仍然有效)。将内容获取逻辑移入单独的'use cache'helper,并使用cacheLife('hours')和cacheTag,然后从页面或GET中调用它(该指令不能放在GET导出本身上);发布 webhook 则调用revalidateTag(tag, 'max')。重新验证不会清除应用前面的 CDN 缓存;请在同一个 webhook 中清除 CDN 缓存。 generateSitemaps()会生成/product/sitemap/0.xml、/product/sitemap/1.xml,但不会生成索引文件。在robots.ts中列出每个生成的文件(sitemap接受数组),或在app/sitemap.xml/route.ts中手动编写索引;否则 Google 只会看到它偶然发现的文件。- 在站点地图的每一行上都设置
lastModified: new Date(),会让整个站点在每次部署时都被标记为有变更;这样 Google 就会在全站忽略lastmod。priority和changeFrequency会被直接忽略,因此输出它们毫无收益,只会让差异内容更杂乱。 robots.txt中不要添加Host:行。Yandex 在 2018 年弃用了该指令,改为使用 301 重定向到首选主机,而next.config.ts已经提供了这种重定向;审核工具还会将残留行标记为未知指令。不要在robots.ts的返回值中包含host。- 当子分段声明对象值元数据(
openGraph、twitter、robots、alternates)时,Next.js 会整体替换该对象,而不是深度合并。添加openGraph: { title }的页面会丢失继承而来的siteName、images和locale,而images: undefined也算作一次声明。在路由中添加这些代码块时,父级提供的每个字段都必须由你重新声明。标量(title、description、authors、creator)则会正常合并。 title.template只应用于<title>。og:title会根据openGraph.title.template解析,这是另一套独立机制,因此从页面标题中删除手动添加的后缀,会悄悄地把产品名称也从分享卡片中删掉。在布局中设置这两个模板,或者都不设置。嵌套布局中的纯字符串title还会将该子树的父模板重置为 null;应改为返回{ default, template }。- 永远不要在根布局中设置
openGraph.url。它不是逐页设置的,每条路由都会继承它,所有分享都会指向首页 URL。子级无法修正它而不声明openGraph并丢失该代码块的其他内容。缺省总比错误好:使用方会回退到其抓取的 URL,而且alternates.canonical本来就是逐页设置的。 middleware.ts已在 Next.js 16 中弃用。新的请求时逻辑(slug 检查、CSP nonce、Accept协商)应放在proxy.ts中,并导出proxy函数;npx @next/codemod@canary middleware-to-proxy .可迁移现有文件。Proxy 仅支持 Node.js;runtime不是有效的导出项。next.config.ts中的重定向按数组顺序匹配,:path*会吞掉其下的所有路径,因此,放在兜底规则之后的特定来源路径永远不会生效;而裸路径/old必须放在/old/:path*之前,否则它会落到/new/。Vercel 将规则数量上限设为 1,024;超过后,将映射移至proxy.ts。- Next 会接管 App Router HTML 响应上的
Vary标头,并用自己的 RSC 列表(rsc, next-router-state-tree, ...)替换它。在proxy.ts的NextResponse.next()中或headers()中设置的Vary: Accept都不会传到客户端。它只有在你自行构造的Response上才会保留,因此,基于内容协商的 Markdown 必须直接返回,而不能重写到其他路径。 - 在 Suspense 边界或
loading.tsx已经输出内容后调用notFound(),会返回带有noindexmeta 的 200,而不是 404。未启用 Cache Components 时,应在第一次可能挂起的await之前调用;启用 Cache Components 后,文档明确说明每条动态路由都会先输出静态外壳,因此存在性检查应移至proxy.ts。否则,分析工具和运行状态监控会将不存在的页面视为正常的 200。真实 404 上的noindexmeta 会由 Next.js 自动注入,包括global-not-found;因此,在 404 页面中添加robots不会改变任何内容。 - 在流式传输开始后调用
redirect()和permanentRedirect(),会输出客户端<meta>重定向并返回 200,而不是 307 或 308。在 Cache Components 下,于页面中处理已迁移的 slug,会以 200 加 meta refresh 的形式发送;永久重定向应放在next.config.ts或 Proxy 中。 - 仅支持 HTML 的爬虫(
Twitterbot、Bingbot、facebookexternalhit和 Next 的默认列表)会跳过预渲染外壳,并在请求时动态渲染整个页面。外壳中只在构建时读取的内容(例如部署后的函数中不存在的fs读取结果、构建时环境变量)可能会导致爬虫失败,而页面对普通用户却能正常加载。curl -A Twitterbot可测试这条路径;每条涉及文件系统的路由都要运行该测试。 - 对包含动态
generateMetadata的页面运行curl -s <url> | rg canonical时,可能会在<body>中找到该标签,因为 Next.js 会为非 bot 用户代理流式传输元数据。这对 Googlebot 来说是有效的。要查看阻塞式<head>形式,请传入-A Twitterbot。 - nonce CSP 会强制每个页面都采用动态渲染:不能静态生成、不能使用 ISR、不能输出预渲染外壳,而且它与 Partial Prerendering 完全不兼容。为修复扫描器警告而添加它,会让站点失去缓存;除非合规要求禁止
'unsafe-inline',否则应使用静态的next.config.ts标头。 - 在兜底
headers()规则中设置Cross-Origin-Resource-Policy: same-origin,会阻止所有社交平台爬虫获取 OG 图片,因为它们会跨源抓取图片。为/opengraph-image添加靠后的规则,并设置cross-origin;对于每个标头键,最后一条匹配规则会生效。 app/opengraph-image.tsx的服务路径是/opengraph-image,而不是/opengraph-image.png。在openGraph.images中硬编码扩展名,会产生一个标记看似正确、实际却无法使用的分享卡片。在basePath下,Next 会给静态的app/opengraph-image.png加上 base path,但完全不会给生成的.tsx路由加前缀,因此,包含 base path 的metadataBase会让 PNG 形式出现重复前缀(/glide/glide/opengraph-image.png),而生成形式则正确。应从next build输出中读取实际生成的og:image,不要用next dev,因为它会将metadataBase改写为开发环境的 origin。.well-known发现文件的文档规定位置是app/.well-known/<name>/route.ts中的路由处理程序;trailingSlash也会对.well-known/下的所有路径豁免。以下方式则不起作用:从public/.well-known/提供文件(静态处理流程会丢弃点文件),或通过next.config.tsrewrite 访问类似/.well-known/mcp.json的单段带点路径;有一次 16.x 部署发现这种写法返回了预渲染的 404,而双段路径/.well-known/mcp/server-card.json的 rewrite 则正常。如果必须使用 rewrite,请在proxy.ts中使用字面路径配置 matcher。- Proxy matcher 的起点和终点都会锚定。单独的
/about条目只匹配/about,不会匹配其下的路径;/writing/:slug只匹配一段路径。文档的 matcher 页面称/about也会匹配/about/team,但 16.3.0 中的正则getMiddlewareMatchers编译结果以[\/#\?]?$结尾。请列出每个需要协商的路径,或使用:path*。 - 静态预渲染的路由处理程序(
sitemap.ts、robots.ts、llms.txt)会在构建时运行,此后只有缓存重新验证时才会再次运行,因此,其中的分析数据采集每次部署只会触发一次。逐请求采集应放在proxy.ts中,并使用event.waitUntil()或after()。而且GET处理程序中的try/catch也会捕获预渲染中止;应设置experimental.hideLogsAfterAbort: true,否则每个缓存处理程序都会在构建期间输出堆栈跟踪。 next/image在 16 中弃用了priority,改用preload;对于 LCP 主视觉图,文档更推荐loading="eager"或fetchPriority="high",而preload应留给必须在 HTML 完成解析前开始加载的那一张图片。LCP 元素应放在每个<Suspense>边界之外或上方;骨架屏的尺寸应与其替代的内容一致;并通过小型客户端组件中的useReportWebVitals发送字段数据。- 为避免出现在 ChatGPT 的回答中而屏蔽
GPTBot,实际效果与意图相反:GPTBot仅用于训练;将页面纳入 ChatGPT 搜索的是OAI-SearchBot;而ChatGPT-User会在用户提问时实时抓取。ClaudeBot与Claude-SearchBot、Claude-User之间的关系也相同。 - 禁止
Google-Extended不会让页面退出 AI Overviews;它只会撤回 Gemini 训练和信息参考权限。退出 AI Overviews 的方式是设置nosnippet或max-snippet:0,但这也会移除普通摘要。 - 对于 2025 年 7 月后创建的区域,Cloudflare 默认会屏蔽已知的 AI 爬虫,并在
robots.txt前置自己的Content-Signal屏蔽规则。在编辑robots.ts之前,应对线上主机运行curl -A OAI-SearchBot;代码即使正确,站点仍可能无法被引用。 - 在非生产分支的自定义域名上,Vercel 会省略
X-Robots-Tag: noindex。如果headers()规则没有添加该标头,staging.example.com就会被编入索引。 - 对重复页面同时设置
noindex和指向生产页面的 canonical,会适得其反:Google 不会从它被告知不要编入索引的页面进行合并。重复页面只用 canonical;预览页面只用noindex。 - Lighthouse 通过只代表在一台设备上完成了一次实验室测试。Google 评估的是 CrUX 字段数据的 p75,因此 Lighthouse 得分为绿色但字段数据评估不合格的情况很常见。应同时报告两者,并指出字段数据中未通过的指标。
- 审核中绝不要编造 Search Console 覆盖率、抓取统计或流量数据;否则修复会落在错误的页面上。应写明“无数据”,并指出缺少哪项访问权限。
- 页面没有资格使用的结构化数据(没有可见问题却使用
FAQPage、没有署名作者却使用Person、没有评论却使用Review)会被视为垃圾内容。FAQPage的富媒体搜索结果已于 2026 年 5 月 7 日取消,因此过去可以抵消风险的收益已不复存在。JSON-LD 中的每项声明都必须能在已提供的 DOM 中得到验证:答案从未渲染出来的 FAQ,或列出了可见导航轨迹中不存在条目的BreadcrumbList,都违反结构化数据政策;答案引擎引用的是 DOM,而不是脚本。应检查构建后的 HTML,而不是源代码;针对"@type":"FAQPage"的子串测试会让有问题的页面通过。 JSON.stringify不会转义<。每次都应像 Next.js 的 JSON-LD 指南那样,在 JSON-LD 序列化器中将其替换为\u003c转义;逐个判断字段是否可能包含 HTML,迟早会因为后续的内容变更而引发脚本注入。- Google 的 Article 文档没有必填字段,
publisher也是可选的。如果包含该字段,应将其设为带有logo的 Organization;Person类型的 publisher 无法通过 Rich Results Test 的警告检查。 - 将可编入索引的内容移至
"use client",并在useEffect中获取数据,会让所有不运行 JavaScript 的爬虫都看不到内容;这包括所有 AI Agents 和 Googlebot 首次抓取。Server Components 本来就会在服务器上渲染;这是你主动引入的退步。 hreflang不互为对应(某一页面缺少另一个页面的 alternate)会导致整组配置被丢弃。canonical 指向其他语言的页面会导致本地化页面被丢弃。- 在所有子域名都启用 HTTPS 之前就设置带有
preload的Strict-Transport-Security,实际上无法撤销:移除后仍需数月才能传达到浏览器。应先逐步将max-age提高到 300、604800 和 2592000。 - 在 EEA/英国流量中投放 Google Ads 或 GA4 标签,却没有
ad_user_data和ad_personalization同意信号(Consent Mode v2,自 2024 年 3 月起为强制要求),会悄悄降低转化衡量的准确性;即使横幅设置完美,数据仍可能有误。 - 维护窗口期间返回 200 或 404,长时间停机后会导致站点从索引中移除。应返回 503 并附带
Retry-After。
相关技能
seo-program:关键词和提示词搜索量研究、问题图谱、作者简报、Search Console 监控。它负责决定页面应以什么为目标;本技能负责构建页面并报告已交付的内容。ui-design:视觉方向和落地页 CRO(Direction 模式)、页面构建(Build 模式),以及页面级 UI 质量(包括渲染后的 i18n 行为,Audit 模式)。此技能负责hreflang和本地化元数据。multi-tenant-architecture:按租户路由robots.txt、sitemap.xml和llms.txt,这些文件必须动态提供,绝不能来自/public。路由正常工作后,此技能负责其内容。docs-writing:决定文档站点应在llms.txt中列出哪些内容,以及哪些页面值得提供 Markdown 替代版本。此技能负责路由和爬虫策略;该技能负责确定哪些文档值得链接。scaffold-nextjs:首次部署后交接给此技能。其默认设置会启用 Cache Components,从而改变站点地图的重新验证方式(参见注意事项)。copywriting:在此技能确定关键词重点和长度后,撰写元描述和标题。- 外部
ghostwriter(使用 blog 配置):撰写文章。
验证
针对已提供的生产构建,运行 references/validation-evidence.md 中适用的检查。记录命令、结果、准确 URL 和未解决的阻碍项。第 5 步会同时加载验证参考和 references/seo-checklist.md。
仅用于维护:evals/evals.json 包含针对本技能变更的回归场景;用户任务期间不会加载。