Prompt-to-PR: Agregar un Sitemap y robots.txt
SOP para agregar un sitemap XML dinámico y robots.txt a un proyecto Next.js o Astro — lastmod, prioridad y reglas de rastreo correctas para SEO en producción.
CursorClaude CodeCodexWindsurf Next.jsAstroTypeScript
Los sitemaps y robots.txt son las primeras primitivas SEO que un agente toca, y a menudo son incorrectos — formato lastmod equivocado, directiva Sitemap: faltante en robots, o páginas bloqueadas incluidas inadvertidamente. Este manual los hace correctos.
1. Requisito
Producir un sitemap XML que cubra todas las rutas públicas (contenido estático + dinámico de la base de datos) y un robots.txt que bloquee rutas de administración/API y haga referencia al sitemap. Funciona tanto para Next.js App Router como para Astro; elige el enfoque correcto para tu framework.
2. Primer Prompt
Add a sitemap.xml and robots.txt to this project. Use the correct approachfor the framework detected below.
### If Next.js 14+:1. Create `src/app/sitemap.ts` using the Next.js `MetadataRoute.Sitemap` return type. Include: - All static routes: /, /pricing, /blog, /about (hardcoded is fine). - All dynamic blog posts: fetch slugs from the DB using the existing query helper, return lastModified from the post's updatedAt field. - Use `process.env.NEXT_PUBLIC_APP_URL` as the base URL. - Correct W3C datetime format for lastModified (ISO 8601).2. Create `src/app/robots.ts` using MetadataRoute.Robots. - Allow: all routes. - Disallow: /admin, /api, /dashboard. - Add `sitemap: process.env.NEXT_PUBLIC_APP_URL + "/sitemap.xml"`.
### If Astro:1. Add `@astrojs/sitemap` integration. In astro.config.ts, add `sitemap({ filter: (page) => !page.includes("/admin") })` and set `site: process.env.SITE_URL`.2. Create `public/robots.txt`: User-agent: * Disallow: /admin Disallow: /api Sitemap: <SITE_URL>/sitemap-index.xml
Do not create a custom sitemap endpoint if the integration handles it.Do not block / or any public content pages.3. Cambios de Archivos Esperados
### Next.jssrc/app/sitemap.ts (new — dynamic MetadataRoute.Sitemap)src/app/robots.ts (new — MetadataRoute.Robots)
### Astroastro.config.ts (add sitemap integration + filter)public/robots.txt (new).env.example (SITE_URL added if missing)4. Lista de Verificación
- La URL base proviene de una variable de entorno — no codificada como
http://localhost:3000. lastModifiedes un objetoDatede JavaScript (Next.js lo convierte a ISO 8601) o ya una cadena ISO válida — no"undefined"o faltante./admin,/api, y/dashboardestán en la lista Disallow.- La directiva
Sitemap:enrobots.txtusa una URL absoluta. - Las rutas dinámicas (publicaciones de blog) se incluyen mediante una consulta a la base de datos, no solo rutas estáticas.
- El sitemap no incluye páginas 404, de redirección o noindex.
bun run buildy luegocurl /sitemap.xmldevuelve XML válido (verificar conxmllint).
5. Comandos de Prueba
bun run build && bun run start# or for Astro:bun run build && bun run preview
# Validate sitemap XMLcurl -s http://localhost:3000/sitemap.xml | xmllint --format - | head -40
# Confirm robots.txtcurl http://localhost:3000/robots.txt
# Confirm admin is disallowed and sitemap directive is presentgrep -E "Disallow|Sitemap" <(curl -s http://localhost:3000/robots.txt)
# Google Rich Results / URL Inspection simulationcurl -A "Googlebot" http://localhost:3000/sitemap.xml -I6. Fallos Comunes
lastModifiedes"undefined"— el campoupdatedAtde la publicación es nulo. Protección:lastModified: post.updatedAt ?? post.createdAt ?? new Date().- El sitemap devuelve 404 —
src/app/sitemap.tsfalta o está fuera del directorioapp. - Todas las rutas deshabilitadas — el agente agrega
Disallow: /por error. Confirma que solo las rutas de administración/API están bloqueadas. - La directiva
Sitemap:tiene URL relativa — Google la ignora. Debe ser absoluta:https://example.com/sitemap.xml. - Solo sitemap estático — el agente codifica slugs de blog en lugar de consultar la base de datos. Confirma que la función del sitemap es
asyncy obtiene datos reales.
7. Prompt de Corrección
The sitemap.xml at /sitemap.xml includes every blog post withlastModified "undefined" (rendered as the string).
Fix in src/app/sitemap.ts: const posts = await getBlogPosts(); return posts.map((post) => ({ url: `${BASE_URL}/blog/${post.slug}`, lastModified: post.updatedAt ?? post.createdAt ?? new Date(), changeFrequency: "weekly", priority: 0.8, }));
Ensure getBlogPosts() returns rows that include updatedAt and createdAt.8. Descripción de PR
## SEO: Add dynamic sitemap.xml and robots.txt
**Next.js**: `src/app/sitemap.ts` + `src/app/robots.ts` using built-in`MetadataRoute` types. Sitemap includes static routes + all published blogposts with correct `lastModified` timestamps from the DB.
**robots.txt** disallows `/admin`, `/api`, `/dashboard`; includes absolute`Sitemap:` directive pointing to the generated `/sitemap.xml`.
Base URL read from `NEXT_PUBLIC_APP_URL` — no localhost URLs in production.