{
  "id": "ai-ignores-seo-metadata",
  "type": "failures",
  "category": "failures",
  "locale": "fr",
  "url": "/fr/failures/ai-ignores-seo-metadata",
  "title": "Comment corriger l'ignorance des métadonnées SEO par l'IA",
  "description": "Les agents IA génèrent des pages Next.js et Astro sans balises title, métadonnées Open Graph ni URL canoniques, produisant des pages invisibles pour les moteurs de recherche et les crawlers sociaux.",
  "tools": [
    "Cursor",
    "Claude Code",
    "Codex",
    "Windsurf"
  ],
  "stack": [
    "Next.js",
    "Astro",
    "TypeScript"
  ],
  "tags": [
    "seo",
    "nextjs",
    "astro"
  ],
  "difficulty": null,
  "updated": "2026-06-08",
  "markdown": "L'agent génère des composants de page sans export `Metadata`, sans balise `title` et sans balises Open Graph, produisant des pages qui se classent mal et se partagent mal sur les réseaux sociaux.\n\n## Le symptôme\n\nUne page Next.js App Router est générée sans aucun export de métadonnées, laissant le titre de l'onglet vide et le `head` vide en dehors des valeurs par défaut de 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 voit : `Untitled document`. Twitter/LinkedIn affichent une carte vide.\n\n## Pourquoi cela se produit\n\nL'agent se concentre sur l'interface utilisateur visible — la récupération et le rendu des données — et traite les métadonnées `head` comme une réflexion après coup. Le SEO est invisible pendant le développement local et ne produit pas d'erreur d'exécution, donc l'agent ne reçoit jamais de retour indiquant qu'il manque.\n\n## Comment le repérer\n\n- Pas de `export const metadata` ou `export async function generateMetadata` dans les fichiers de page App Router.\n- Pas de `Astro.props` ou de `title` / `description` pilotés par le frontmatter dans les layouts Astro.\n- Score SEO Lighthouse inférieur à 90 sur une nouvelle page.\n- `curl -s http://localhost:3000/blog/my-post | grep \"<title>\"` ne renvoie rien ou un titre de site générique.\n- `og:image` manquant dans le `head` (l'aperçu social montre une carte vide).\n\n## Comment le corriger\n\nExportez `generateMetadata` pour les pages dynamiques et ajoutez toutes les balises nécessaires.\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\nPour Astro, passez les métadonnées via le layout :\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## Invite de correction\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## Test\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```"
}