Saltar al contenido

robots.txt, canonical y Open Graph derivados del contenido

Cerrar el nivel de datos derivados con las señales para máquinas: generar robots.txt estático o por endpoint con su línea Sitemap, emitir una URL canonical por página desde Astro.site y Astro.url, derivar las etiquetas Open Graph del frontmatter del contenido con imágenes absolutas, y la regla de una única fuente de verdad.

robots.txt: estático o generado

Lo más simple es un fichero public/robots.txt, que Astro sirve tal cual en /robots.txt. Ahí declaras qué rastreadores pueden entrar con User-agent, qué rutas evitar con Disallow, y —lo más útil— una línea Sitemap con la URL absoluta de tu mapa. El problema del fichero estático es que no conoce tu site: la URL del sitemap queda escrita a mano, y si cambias de dominio, se queda mintiendo.

La alternativa es generarlo desde un endpoint src/pages/robots.txt.ts que devuelve texto plano y lee context.site. Así la URL del sitemap se deriva de la configuración en lugar de copiarse, y sigue siendo verdadera aunque cambies de dominio.

TypeScript
// src/pages/robots.txt.tsimport type { APIRoute } from 'astro';const cuerpo = (site: string) => `User-agent: *Allow: /Disallow: /admin/Sitemap: ${new URL('sitemap-index.xml', site).href}`.trim();export const GET: APIRoute = ({ site }) => {  return new Response(cuerpo(site!.href), {    headers: { 'Content-Type': 'text/plain' },  });};

Como la ruta de endpoint gana, no necesitas el fichero estático: la línea Sitemap se construye con new URL contra site, y hay una sola fuente de verdad para el dominio. Cambiar site en la configuración actualiza el robots.txt, el sitemap y todo lo demás a la vez.

Conviene recordar qué es y qué no es el robots.txt: una sugerencia que los rastreadores educados respetan, no un muro. Disallow esconde una ruta del índice, pero no la protege —cualquiera puede leer el fichero y visitar justo lo que ocultas—, así que nunca es el sitio para secretos. Su valor real es orientar el rastreo hacia lo que importa y, sobre todo, anunciar el sitemap para que el buscador encuentre el mapa completo sin depender de tropezar con los enlaces.

canonical: una URL verdadera por página

Un buscador puede llegar a la misma página por varias direcciones —con barra final y sin ella, con parámetros de campaña, por http y por https— y tratarlas como contenido duplicado, repartiendo la autoridad entre copias. La etiqueta <link rel="canonical"> corta ese nudo nombrando la única dirección verdadera. Se construye combinando Astro.site, tu dominio configurado, con Astro.url.pathname, la ruta actual, en un solo new URL.

ASTRO
---const canonical = new URL(Astro.url.pathname, Astro.site);---<link rel="canonical" href={canonical.href} />

Lo importante es derivar la URL en vez de escribirla. Una canonical a mano en cada página es una copia que se desincroniza; una derivada de Astro.site y Astro.url es correcta por construcción en cualquier entorno —desarrollo, pruebas, producción— porque se recalcula de la fuente. Colocar esta línea en el componente de cabecera que comparten todas las páginas garantiza que ninguna se quede sin ella.

Un matiz fácil de pasar por alto: la canonical debe casar con la forma real de tus URLs —con barra final o sin ella—, la misma que fija trailingSlash. Una canonical que apunta a una variante que tu sitio no sirve reparte de nuevo la autoridad que pretendía unificar. Derivarla de Astro.url en lugar de componerla a mano evita justo ese desajuste, porque toma la ruta tal como el sitio la resuelve, no como tú crees que se resuelve.

Open Graph derivado del frontmatter

Cuando alguien comparte tu página en una red social, un rastreador lee las etiquetas Open Graph del <head> para componer la tarjeta: título, descripción, imagen. Lo natural es derivarlas del mismo frontmatter que ya gobierna la página —og:title del title, og:description de la description, og:url de la canonical— en el componente de cabecera que recibe esas props. Una sola plantilla, alimentada por el contenido, y todas las páginas quedan cubiertas.

ASTRO
---interface Props {  title: string;  description: string;  image?: string;}const { title, description, image = '/og-por-defecto.png' } = Astro.props;const canonical = new URL(Astro.url.pathname, Astro.site);const ogImage = new URL(image, Astro.site);---<meta property="og:title" content={title} /><meta property="og:description" content={description} /><meta property="og:url" content={canonical.href} /><meta property="og:image" content={ogImage.href} /><meta property="og:type" content="website" />

Las etiquetas twitter:card y sus hermanas replican esta información para la tarjeta de X, y a menudo basta con declarar twitter:card y dejar que reutilice las og:. Conviene que la imagen sea de proporción ancha —1200 por 630 es el estándar— para que ninguna red la recorte. Un paso más allá, los datos estructurados en JSON-LD, un bloque <script type="application/ld+json"> con el artículo, su autor y su fecha, le dan al buscador la misma verdad en un formato que entiende para los resultados enriquecidos. Todo, otra vez, derivado del mismo frontmatter: una fuente, muchas etiquetas.

ASTRO
---const jsonLd = {  '@context': 'https://schema.org',  '@type': 'Article',  headline: title,  datePublished: Astro.props.date,  author: { '@type': 'Person', name: Astro.props.author },};---<script type="application/ld+json" set:html={JSON.stringify(jsonLd)} />