{
  "id": "ai-ignores-seo-metadata",
  "type": "failures",
  "category": "failures",
  "locale": "zh",
  "url": "/zh/failures/ai-ignores-seo-metadata",
  "title": "如何修复AI忽略SEO元数据",
  "description": "AI代理在构建Next.js和Astro页面时，缺少标题标签、Open Graph元数据或规范URL，导致页面在搜索引擎和社交爬虫中不可见。",
  "tools": [
    "Cursor",
    "Claude Code",
    "Codex",
    "Windsurf"
  ],
  "stack": [
    "Next.js",
    "Astro",
    "TypeScript"
  ],
  "tags": [
    "seo",
    "nextjs",
    "astro"
  ],
  "difficulty": null,
  "updated": "2026-06-08",
  "markdown": "代理生成的页面组件没有导出`Metadata`，没有`title`标签，也没有Open Graph标签，导致页面在搜索引擎中排名不佳，在社交媒体上分享效果也不好。\n\n## 症状\n\nNext.js App Router页面在构建时没有导出任何元数据，导致标签页标题空白，`head`中除了Next.js默认内容外为空。\n\n```tsx\n// app/blog/[slug]/page.tsx — WRONG\nexport default async function BlogPost({ params }: { params: { slug: string } }) {\n  const post = await getPost(params.slug);\n  return (\n    <article>\n      <h1>{post.title}</h1>\n      <div dangerouslySetInnerHTML={{ __html: post.html }} />\n    </article>\n  );\n  // No <title>, no meta description, no og:image, no canonical URL\n}\n```\n\nGoogle看到：`Untitled document`。Twitter/LinkedIn显示空白卡片。\n\n## 原因\n\n代理专注于可见的UI——数据获取和渲染——而将`head`元数据视为事后考虑。SEO在本地开发中不可见，也不会产生运行时错误，因此代理从未收到缺失的反馈。\n\n## 如何发现\n\n- App Router页面文件中没有`export const metadata`或`export async function generateMetadata`。\n- Astro布局中没有`Astro.props`或由前置元数据驱动的`title`/`description`。\n- 新页面的Lighthouse SEO评分低于90。\n- 执行`curl -s http://localhost:3000/blog/my-post | grep \"<title>\"`返回空或通用网站标题。\n- `head`中缺少`og:image`（社交预览显示空白卡片）。\n\n## 如何修复\n\n为动态页面导出`generateMetadata`并添加所有必需的标签。\n\n```tsx\n// app/blog/[slug]/page.tsx — CORRECT\nimport type { Metadata } from \"next\";\n\nexport async function generateMetadata({\n  params,\n}: {\n  params: { slug: string };\n}): Promise<Metadata> {\n  const post = await getPost(params.slug);\n  return {\n    title: post.title,\n    description: post.excerpt,\n    alternates: { canonical: `https://example.com/blog/${params.slug}` },\n    openGraph: {\n      title: post.title,\n      description: post.excerpt,\n      url: `https://example.com/blog/${params.slug}`,\n      images: [{ url: post.ogImage, width: 1200, height: 630, alt: post.title }],\n      type: \"article\",\n      publishedTime: post.publishedAt,\n    },\n    twitter: {\n      card: \"summary_large_image\",\n      title: post.title,\n      description: post.excerpt,\n      images: [post.ogImage],\n    },\n  };\n}\n\nexport default async function BlogPost({ params }: { params: { slug: string } }) {\n  const post = await getPost(params.slug);\n  return (\n    <article>\n      <h1>{post.title}</h1>\n      <div dangerouslySetInnerHTML={{ __html: post.html }} />\n    </article>\n  );\n}\n```\n\n对于Astro，通过布局传递元数据：\n\n```astro\n---\n// src/pages/blog/[slug].astro\nconst { post } = Astro.props;\n---\n<Layout\n  title={post.title}\n  description={post.excerpt}\n  ogImage={post.ogImage}\n  canonical={`https://example.com/blog/${post.slug}`}\n>\n  <article>\n    <h1>{post.title}</h1>\n    <post.Content />\n  </article>\n</Layout>\n```\n\n```txt\n[ ] Every page exports generateMetadata (Next.js) or passes title+description to layout (Astro)\n[ ] title is unique per page — not just the site name\n[ ] description is 120-160 characters and describes the page content\n[ ] og:title, og:description, og:image (1200x630), og:url are all set\n[ ] twitter:card is \"summary_large_image\" for pages with images\n[ ] canonical URL is set to the preferred URL (no trailing slash ambiguity)\n[ ] robots meta is not accidentally set to \"noindex\"\n[ ] Verify with: curl -s <url> | grep -E \"<title>|og:title|description\"\n```\n\n## 修复提示\n\n```txt title=\"Fix Prompt\"\nThis page component is missing all SEO metadata. Add an async generateMetadata\nexport (Next.js App Router) that returns title, description, canonical URL, full\nopenGraph object (title, description, url, images with 1200x630 dimensions, type),\nand twitter card metadata. Derive values from the page's data fetch — do not use\nplaceholder strings. Also confirm that no parent layout accidentally sets\nrobots: noindex.\n```\n\n## 测试\n\n```bash\n# Check that <title> and og:title appear in the server-rendered HTML\ncurl -s http://localhost:3000/blog/my-post \\\n  | grep -E '<title>|og:title|og:description|og:image|canonical' \\\n  | grep -v \"node_modules\" \\\n  && echo \"Metadata present\" || echo \"FAIL: metadata missing\"\n```"
}