mcpField
N
nextjs-seo-optimizer

nextjs-seo-optimizer

github.com/kumbajirajkumar123营销与增长273 次安装40 周前更新
代码仓库

关于 nextjs-seo-optimizer

面向 Next.js 应用开发者的 SEO 实施与审计技能,涵盖元数据、爬取控制、结构化数据、性能和验证,并提供 App Router 与旧版 Pages Router 的指导。

主要功能

  1. 01静态与动态元数据
  2. 02配置站点地图和 robots.txt
  3. 03JSON-LD 结构化数据
  4. 04Core Web Vitals 优化指导
  5. 05SEO 审计与验证清单

适用场景

  1. 01为静态和动态页面设置 SEO 元数据
  2. 02创建站点地图和爬虫规则
  3. 03为内容页面添加结构化数据
  4. 04审计技术 SEO 与性能

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/kumbajirajkumar123/nextjs-seo-optimizer --skill nextjs-seo-optimizer

安装后在 Claude Code 中输入/nextjs-seo-optimizer确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

.gitignore
LICENSE
CONTRIBUTING.md
CHANGELOG.md
README.md
INSTALLATION.md
.github/workflows/
validate.yml
release.yml
references/
complete-example.md
next-sitemap-guide.md
performance-optimization.md
pages-router-seo.md
scripts/
validate-seo.js

详细介绍

Next.js SEO 优化器

通过配置合适的元数据、站点地图、robots.txt 和结构化数据,为 Next.js 应用优化搜索引擎表现。

SEO 核心实施策略

按以下顺序实施,以获得最大效果:

  1. 设置 Metadata API(基础)— 标题、描述、Open Graph
  2. 生成站点地图(发现)— 帮助搜索引擎找到所有页面
  3. 配置 Robots.txt(控制)— 管理爬虫访问权限
  4. 结构化数据(丰富结果)— 启用丰富摘要
  5. 性能优化(排名)— Core Web Vitals
  6. 内部链接(架构)— 网站结构和流转

1. Metadata API 实现

静态元数据(固定内容页面)

用于内容不变的页面(首页、关于、联系):

// app/layout.tsx or app/page.tsx
import { Metadata } from 'next'

export const metadata: Metadata = {
  title: {
    default: 'Your Site Name',
    template: '%s | Your Site Name' // Pages will be "Page Title | Your Site Name"
  },
  description: 'Compelling 150-160 character description with keywords',
  keywords: ['keyword1', 'keyword2', 'keyword3'],
  authors: [{ name: 'Author Name', url: 'https://example.com' }],
  creator: 'Company Name',
  publisher: 'Company Name',
  metadataBase: new URL('https://yourdomain.com'),
  alternates: {
    canonical: '/',
  },
  openGraph: {
    type: 'website',
    locale: 'en_US',
    url: 'https://yourdomain.com',
    title: 'Your Site Name',
    description: 'Compelling description for social sharing',
    siteName: 'Your Site Name',
    images: [{
      url: '/og-image.jpg', // 1200x630px recommended
      width: 1200,
      height: 630,
      alt: 'Image description',
    }],
  },
  twitter: {
    card: 'summary_large_image',
    title: 'Your Site Name',
    description: 'Compelling description',
    creator: '@yourtwitterhandle',
    images: ['/twitter-image.jpg'], // 1200x628px
  },
  robots: {
    index: true,
    follow: true,
    googleBot: {
      index: true,
      follow: true,
      'max-video-preview': -1,
      'max-image-preview': 'large',
      'max-snippet': -1,
    },
  },
  verification: {
    google: 'verification-code',
    yandex: 'verification-code',
    yahoo: 'verification-code',
  },
}
动态元数据(数据库/CMS 内容)

用于博客文章、产品和动态页面:

// app/blog/[slug]/page.tsx
import { Metadata, ResolvingMetadata } from 'next'

type Props = {
  params: { slug: string }
  searchParams: { [key: string]: string | string[] | undefined }
}

export async function generateMetadata(
  { params, searchParams }: Props,
  parent: ResolvingMetadata
): Promise<Metadata> {
  // Fetch data
  const post = await fetchBlogPost(params.slug)
  
  // Optionally access and extend parent metadata
  const previousImages = (await parent).openGraph?.images || []
  const previousKeywords = (await parent).keywords || []
 
  return {
    title: post.title, // Will use template from layout
    description: post.excerpt.substring(0, 160),
    keywords: [...previousKeywords, ...post.tags],
    authors: [{ name: post.author.name }],
    openGraph: {
      title: post.title,
      description: post.excerpt,
      type: 'article',
      publishedTime: post.publishedAt,
      modifiedTime: post.updatedAt,
      authors: [post.author.name],
      images: [post.coverImage, ...previousImages],
      url: `https://yourdomain.com/blog/${params.slug}`,
    },
    twitter: {
      card: 'summary_large_image',
      title: post.title,
      description: post.excerpt,
      images: [post.coverImage],
    },
    alternates: {
      canonical: `https://yourdomain.com/blog/${params.slug}`,
    },
  }
}

export default function Page({ params }: Props) {
  // Page content
}
元数据最佳实践

标题优化:

  • 长度:50–60 个字符(移动端 SERP 显示限制)
  • 将主要关键词放在靠前位置
  • 使用模板以保持一致:%s | Brand Name
  • 避免关键词堆砌
  • 确保每个页面的标题各不相同

描述优化:

  • 长度:150–160 个字符(桌面端 SERP 限制),移动端为 120 个字符
  • 自然地包含主要和次要关键词
  • 撰写吸引人的文案,鼓励用户点击
  • 适当加入行动号召
  • 确保每个描述各不相同

图片要求:

  • Open Graph:1200x630px(16:9 比例)
  • Twitter Card:1200x628px(1.91:1 比例)
  • 文件大小:< 1MB
  • 格式:JPG 或 PNG
  • 添加描述性替代文本

2. 生成站点地图

内置静态站点地图(简单网站)

适用于路由可预测的静态网站:

// app/sitemap.ts
import { MetadataRoute } from 'next'
 
export default function sitemap(): MetadataRoute.Sitemap {
  return [
    {
      url: 'https://yourdomain.com',
      lastModified: new Date(),
      changeFrequency: 'yearly',
      priority: 1,
    },
    {
      url: 'https://yourdomain.com/about',
      lastModified: new Date(),
      changeFrequency: 'monthly',
      priority: 0.8,
    },
    {
      url: 'https://yourdomain.com/blog',
      lastModified: new Date(),
      changeFrequency: 'weekly',
      priority: 0.5,
    },
  ]
}
动态站点地图(CMS/数据库内容)

适用于包含来自 CMS 或数据库的动态内容的网站:

// app/sitemap.ts
import { MetadataRoute } from 'next'
 
export default async function sitemap(): MetadataRoute.Sitemap {
  // Fetch dynamic routes
  const posts = await fetch('https://api.example.com/posts').then((res) => res.json())
  const products = await fetch('https://api.example.com/products').then((res) => res.json())
  
  const postEntries: MetadataRoute.Sitemap = posts.map((post: any) => ({
    url: `https://yourdomain.com/blog/${post.slug}`,
    lastModified: new Date(post.updatedAt),
    changeFrequency: 'weekly',
    priority: 0.7,
  }))
  
  const productEntries: MetadataRoute.Sitemap = products.map((product: any) => ({
    url: `https://yourdomain.com/products/${product.slug}`,
    lastModified: new Date(product.updatedAt),
    changeFrequency: 'daily',
    priority: 0.9,
  }))
  
  return [
    {
      url: 'https://yourdomain.com',
      lastModified: new Date(),
      changeFrequency: 'yearly',
      priority: 1,
    },
    ...postEntries,
    ...productEntries,
  ]
}
使用 next-sitemap(大型网站推荐)

有关自动生成的详细设置,请参阅 references/next-sitemap-guide.md。

优先级指南:

  • 首页:1.0
  • 重要落地页:0.8-0.9
  • 博客文章 / 产品:0.6-0.8
  • 分类页面:0.5-0.7
  • 其他页面:0.4-0.6

更新频率指南:

  • always:股价、实时数据
  • hourly:新闻网站、频繁更新的内容
  • daily:博客、活跃的电商网站
  • weekly:常规博客文章
  • monthly:功能页面
  • yearly:静态页面(关于、联系)
  • never:已归档内容

3. 配置 Robots.txt

基础 robots.txt(内置)
// app/robots.ts
import { MetadataRoute } from 'next'
 
export default function robots(): MetadataRoute.Robots {
  return {
    rules: {
      userAgent: '*',
      allow: '/',
      disallow: ['/admin/', '/api/', '/private/'],
    },
    sitemap: 'https://yourdomain.com/sitemap.xml',
  }
}
高级 robots.txt(多个用户代理)
// app/robots.ts
import { MetadataRoute } from 'next'
 
export default function robots(): MetadataRoute.Robots {
  const baseUrl = process.env.NEXT_PUBLIC_BASE_URL || 'https://yourdomain.com'
  
  return {
    rules: [
      {
        userAgent: '*',
        allow: '/',
        disallow: ['/admin/', '/api/', '/_next/', '/private/'],
        crawlDelay: 1,
      },
      {
        userAgent: 'Googlebot',
        allow: '/',
        disallow: ['/admin/', '/private/'],
      },
      {
        userAgent: 'GPTBot', // Block AI scrapers if desired
        disallow: ['/'],
      },
    ],
    sitemap: `${baseUrl}/sitemap.xml`,
    host: baseUrl,
  }
}

常见的禁止访问路径:

  • /admin/ — 管理面板
  • /api/ — API 端点(非公开端点除外)
  • /_next/ — Next.js 内部文件
  • /private/ — 私有/受限内容
  • /search?* — 搜索结果页面(防止内容重复)
  • /cart/ — 购物车页面
  • /checkout/ — 结账页面
  • *?* — 所有包含查询参数的页面(可选,谨慎使用)

特定环境的规则:

const isDevelopment = process.env.NODE_ENV === 'development'
const isStaging = process.env.VERCEL_ENV === 'preview'

if (isDevelopment || isStaging) {
  return {
    rules: {
      userAgent: '*',
      disallow: '/', // Block all indexing in dev/staging
    },
  }
}

4. 结构化数据(JSON-LD)

添加结构化数据以在搜索结果中展示丰富信息。务必在 https://search.google.com/test/rich-results 测试。

组织架构 Schema(首页)
// app/page.tsx or components/StructuredData.tsx
export default function HomePage() {
  const organizationSchema = {
    '@context': 'https://schema.org',
    '@type': 'Organization',
    name: 'Your Company Name',
    url: 'https://yourdomain.com',
    logo: 'https://yourdomain.com/logo.png',
    description: 'Company description',
    address: {
      '@type': 'PostalAddress',
      streetAddress: '123 Main St',
      addressLocality: 'City',
      addressRegion: 'State',
      postalCode: '12345',
      addressCountry: 'US',
    },
    contactPoint: {
      '@type': 'ContactPoint',
      telephone: '+1-234-567-8900',
      contactType: 'Customer Service',
    },
    sameAs: [
      'https://twitter.com/yourcompany',
      'https://facebook.com/yourcompany',
      'https://linkedin.com/company/yourcompany',
    ],
  }

  return (
    <>
      <script
        type="application/ld+json"
        dangerouslySetInnerHTML={{ __html: JSON.stringify(organizationSchema) }}
      />
      {/* Page content */}
    </>
  )
}
文章 Schema(博客文章)
export default function BlogPost({ post }: { post: Post }) {
  const articleSchema = {
    '@context': 'https://schema.org',
    '@type': 'BlogPosting',
    headline: post.title,
    description: post.excerpt,
    image: post.coverImage,
    author: {
      '@type': 'Person',
      name: post.author.name,
      url: `https://yourdomain.com/authors/${post.author.slug}`,
    },
    publisher: {
      '@type': 'Organization',
      name: 'Your Company',
      logo: {
        '@type': 'ImageObject',
        url: 'https://yourdomain.com/logo.png',
      },
    },
    datePublished: post.publishedAt,
    dateModified: post.updatedAt,
    mainEntityOfPage: {
      '@type': 'WebPage',
      '@id': `https://yourdomain.com/blog/${post.slug}`,
    },
  }

  return (
    <>
      <script
        type="application/ld+json"
        dangerouslySetInnerHTML={{ __html: JSON.stringify(articleSchema) }}
      />
      {/* Article content */}
    </>
  )
}
产品 Schema(电商)
const productSchema = {
  '@context': 'https://schema.org',
  '@type': 'Product',
  name: product.name,
  image: product.images,
  description: product.description,
  sku: product.sku,
  brand: {
    '@type': 'Brand',
    name: product.brand,
  },
  offers: {
    '@type': 'Offer',
    url: `https://yourdomain.com/products/${product.slug}`,
    priceCurrency: 'USD',
    price: product.price,
    availability: product.inStock
      ? 'https://schema.org/InStock'
      : 'https://schema.org/OutOfStock',
    seller: {
      '@type': 'Organization',
      name: 'Your Company',
    },
  },
  aggregateRating: {
    '@type': 'AggregateRating',
    ratingValue: product.rating,
    reviewCount: product.reviewCount,
  },
}
FAQ Schema
const faqSchema = {
  '@context': 'https://schema.org',
  '@type': 'FAQPage',
  mainEntity: faqs.map((faq) => ({
    '@type': 'Question',
    name: faq.question,
    acceptedAnswer: {
      '@type': 'Answer',
      text: faq.answer,
    },
  })),
}
面包屑 Schema
const breadcrumbSchema = {
  '@context': 'https://schema.org',
  '@type': 'BreadcrumbList',
  itemListElement: breadcrumbs.map((crumb, index) => ({
    '@type': 'ListItem',
    position: index + 1,
    name: crumb.name,
    item: `https://yourdomain.com${crumb.path}`,
  })),
}

5. Core Web Vitals 优化

这些指标会直接影响搜索排名。详细策略请参阅 references/performance-optimization.md。

快速见效的方法:

  • 使用 Next.js <Image> 组件(自动优化)
  • 在 next.config.js 中启用 experimental.optimizePackageImports
  • 设置合适的缓存标头
  • 对大型组件使用动态导入:const HeavyComponent = dynamic(() => import('./HeavyComponent'))
  • 尽量减少客户端组件('use client')的使用
  • 设置合适的加载状态

6. SEO 审核清单

审核或实施 SEO 时,请检查:

元数据:

  • 所有页面都有唯一标题(50-60 个字符)
  • 所有页面都有唯一描述(150-160 个字符)
  • 在根布局中设置 metadataBase
  • 为所有页面设置规范 URL
  • Open Graph 图片(1200x630px)
  • Twitter Card 图片(1200x628px)
  • 页面之间没有重复的元数据

技术 SEO:

  • 可通过 /sitemap.xml 访问 sitemap.xml
  • 可通过 /robots.txt 访问 robots.txt
  • Sitemap 包含所有公开页面
  • robots.txt 引用了 sitemap
  • 没有意外屏蔽重要页面
  • 已启用并强制使用 HTTPS
  • 404 页面返回 404 状态码
  • 正确使用 301(永久)重定向

结构化数据:

  • 首页包含 Organization 架构
  • 博客文章包含 Article 架构
  • 产品页面包含 Product 架构(如适用)
  • 多层级页面包含 Breadcrumb 架构
  • JSON-LD 有效(使用 Google Rich Results Test 测试)

性能:

  • LCP < 2.5s(最大内容绘制)
  • FID < 100ms(首次输入延迟)
  • CLS < 0.1(累积布局偏移)
  • 图片已优化(WebP、尺寸正确)
  • 关键 CSS 已内联
  • JavaScript 包 < 100KB(初始加载)

内容:

  • 标题层级正确(每页一个 H1)
  • 已实施内部链接策略
  • 所有图片都包含替代文本
  • 设计适配移动设备
  • 符合无障碍标准(WCAG 2.1 AA)

7. 常见 SEO 问题及修复方法

问题:元数据重复

  • 修复:使用 generateMetadata 为每个页面提供唯一数据
  • 修复:实施正确的规范 URL

问题:Core Web Vitals 表现不佳

  • 修复:使用 Next.js Image 组件
  • 修复:实施代码分割
  • 修复:启用 SWC 压缩
  • 参见 references/performance-optimization.md

问题:缺少结构化数据

  • 修复:为相关页面类型添加 JSON-LD
  • 修复:使用 Google Rich Results Test 测试

问题:robots.txt 错误地屏蔽了重要页面

  • 修复:使用 Google Search Console robots.txt 测试工具进行测试
  • 修复:仔细检查 disallow 规则

问题:Sitemap 未更新

  • 修复:使用动态 sitemap 获取数据
  • 修复:实施 next-sitemap 以自动生成

8. 测试与验证工具

使用以下工具验证 SEO 实施情况:

Google 工具:

  • Google Search Console - 监控搜索表现
  • Google Rich Results Test - 验证结构化数据
  • PageSpeed Insights - 测试 Core Web Vitals
  • Lighthouse - 全面审核

第三方工具:

  • Screaming Frog - 抓取网站并识别问题
  • Ahrefs Site Audit - 检查技术 SEO 问题
  • SEMrush Site Audit - 全面分析 SEO
  • Schema Markup Validator - 测试 JSON-LD

测试命令:

# Test metadata locally
curl -I http://localhost:3000/page-to-test

# View sitemap
curl http://localhost:3000/sitemap.xml

# View robots.txt
curl http://localhost:3000/robots.txt

# Run Lighthouse audit
npx lighthouse http://localhost:3000 --view

实施优先级

对于新项目:

  1. 在根布局中设置 metadataBase
  2. 为所有页面添加基本元数据
  3. 创建 sitemap.ts
  4. 创建 robots.ts
  5. 为关键页面添加结构化数据
  6. 优化性能
  7. 实施全面测试

对于现有项目:

  1. 审核当前 SEO(使用 Lighthouse)
  2. 修复关键问题(robots.txt 损坏、缺少 sitemap)
  3. 添加缺失的元数据
  4. 实施结构化数据
  5. 优化 Core Web Vitals
  6. 使用 Google Search Console 进行监控

Next.js 版本兼容性

此技能支持:

  • ✅ Next.js 13+(App Router)——所有功能
  • ⚠️ Next.js 12(Pages Router)——使用旧版模式,参见 references/pages-router-seo.md
  • ❌ Next.js < 12——建议升级

快速入门命令

# Validate sitemap locally
curl http://localhost:3000/sitemap.xml | head -n 20

# Test robots.txt
curl http://localhost:3000/robots.txt

# Run SEO audit
npm run build && npm start
# Then in browser: DevTools > Lighthouse > SEO audit

# Test metadata extraction
curl -s http://localhost:3000 | grep -E '<title>|<meta'

环境变量

在 .env.local 中设置:

NEXT_PUBLIC_BASE_URL=https://yourdomain.com
NEXT_PUBLIC_SITE_NAME="Your Site Name"
NEXT_PUBLIC_OG_IMAGE=https://yourdomain.com/og-image.jpg
GOOGLE_VERIFICATION_CODE=your-verification-code

其他资源

有关具体场景和高级模式:

  • next-sitemap 设置:参见 references/next-sitemap-guide.md
  • 性能优化:参见 references/performance-optimization.md
  • Pages Router 迁移:参见 references/pages-router-seo.md

评论

支持 Markdown