{
  "id": "ai-ignores-seo-metadata",
  "type": "failures",
  "category": "failures",
  "locale": "es",
  "url": "/es/failures/ai-ignores-seo-metadata",
  "title": "Cómo solucionar que la IA ignore los metadatos SEO",
  "description": "Los agentes de IA generan páginas de Next.js y Astro sin etiquetas de título, metadatos de Open Graph ni URL canónicas, entregando páginas invisibles para los motores de búsqueda y los rastreadores sociales.",
  "tools": [
    "Cursor",
    "Claude Code",
    "Codex",
    "Windsurf"
  ],
  "stack": [
    "Next.js",
    "Astro",
    "TypeScript"
  ],
  "tags": [
    "seo",
    "nextjs",
    "astro"
  ],
  "difficulty": null,
  "updated": "2026-06-08",
  "markdown": "El agente genera componentes de página sin exportación de `Metadata`, sin etiqueta `title` y sin etiquetas Open Graph, produciendo páginas que se posicionan mal y se comparten mal en redes sociales.\n\n## El síntoma\n\nUna página de Next.js App Router se genera sin ninguna exportación de metadatos, dejando el título de la pestaña en blanco y el `head` vacío más allá de los valores predeterminados 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 ve: `Untitled document`. Twitter/LinkedIn muestran una tarjeta en blanco.\n\n## Por qué ocurre\n\nEl agente se centra en la interfaz de usuario visible — obtención de datos y renderizado — y trata los metadatos del `head` como algo secundario. El SEO es invisible durante el desarrollo local y no produce un error en tiempo de ejecución, por lo que el agente nunca recibe retroalimentación de que falta algo.\n\n## Cómo detectarlo\n\n- Sin `export const metadata` o `export async function generateMetadata` en los archivos de página de App Router.\n- Sin `Astro.props` ni `title` / `description` impulsados por frontmatter en los diseños de Astro.\n- Puntuación SEO de Lighthouse por debajo de 90 en una página nueva.\n- `curl -s http://localhost:3000/blog/my-post | grep \"<title>\"` no devuelve nada o un título de sitio genérico.\n- `og:image` faltante en el `head` (la vista previa social muestra una tarjeta en blanco).\n\n## Cómo solucionarlo\n\nExporta `generateMetadata` para páginas dinámicas y agrega todas las etiquetas requeridas.\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\nPara Astro, pasa los metadatos a través del diseño:\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## Indicación de solució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## Prueba\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```"
}