{
  "id": "add-a-sitemap-and-robots",
  "type": "playbooks",
  "category": "playbooks",
  "locale": "de",
  "url": "/de/playbooks/add-a-sitemap-and-robots",
  "title": "Prompt-to-PR: Sitemap und robots.txt hinzufügen",
  "description": "SOP zum Hinzufügen einer dynamischen XML-Sitemap und robots.txt zu einem Next.js- oder Astro-Projekt – korrekte lastmod-, priority- und Crawl-Regeln für die Produktions-SEO.",
  "tools": [
    "Cursor",
    "Claude Code",
    "Codex",
    "Windsurf"
  ],
  "stack": [
    "Next.js",
    "Astro",
    "TypeScript"
  ],
  "tags": [
    "seo",
    "nextjs",
    "astro",
    "typescript"
  ],
  "difficulty": "easy",
  "updated": "2026-06-08",
  "markdown": "Sitemaps und `robots.txt` sind die ersten SEO-Grundlagen, die ein Agent berührt, und sie sind häufig falsch – falsches `lastmod`-Format, fehlende `Sitemap:`-Direktive in robots oder versehentlich eingeschlossene blockierte Seiten. Dieses Playbook macht es richtig.\n\n## 1. Anforderung\n\nErstellen Sie eine XML-Sitemap, die alle öffentlichen Routen abdeckt (statische + dynamische Inhalte aus der Datenbank) und eine `robots.txt`, die Admin-/API-Pfade blockiert und auf die Sitemap verweist. Funktioniert sowohl für Next.js App Router als auch Astro; wählen Sie den richtigen Ansatz für Ihr Framework.\n\n## 2. Erste Eingabeaufforderung\n\n```txt title=\"First Prompt\"\nAdd a sitemap.xml and robots.txt to this project. Use the correct approach\nfor the framework detected below.\n\n### If Next.js 14+:\n1. Create `src/app/sitemap.ts` using the Next.js `MetadataRoute.Sitemap`\n   return type. Include:\n   - All static routes: /, /pricing, /blog, /about (hardcoded is fine).\n   - All dynamic blog posts: fetch slugs from the DB using the existing\n     query helper, return lastModified from the post's updatedAt field.\n   - Use `process.env.NEXT_PUBLIC_APP_URL` as the base URL.\n   - Correct W3C datetime format for lastModified (ISO 8601).\n2. Create `src/app/robots.ts` using MetadataRoute.Robots.\n   - Allow: all routes.\n   - Disallow: /admin, /api, /dashboard.\n   - Add `sitemap: process.env.NEXT_PUBLIC_APP_URL + \"/sitemap.xml\"`.\n\n### If Astro:\n1. Add `@astrojs/sitemap` integration. In astro.config.ts, add\n   `sitemap({ filter: (page) => !page.includes(\"/admin\") })` and set\n   `site: process.env.SITE_URL`.\n2. Create `public/robots.txt`:\n   User-agent: *\n   Disallow: /admin\n   Disallow: /api\n   Sitemap: <SITE_URL>/sitemap-index.xml\n\nDo not create a custom sitemap endpoint if the integration handles it.\nDo not block / or any public content pages.\n```\n\n## 3. Erwartete Dateiänderungen\n\n```txt\n### Next.js\nsrc/app/sitemap.ts             (new — dynamic MetadataRoute.Sitemap)\nsrc/app/robots.ts              (new — MetadataRoute.Robots)\n\n### Astro\nastro.config.ts                (add sitemap integration + filter)\npublic/robots.txt              (new)\n.env.example                   (SITE_URL added if missing)\n```\n\n## 4. Überprüfungs-Checkliste\n\n- Die Basis-URL stammt aus einer Umgebungsvariablen – nicht fest codiert als `http://localhost:3000`.\n- `lastModified` ist ein JavaScript-`Date`-Objekt (Next.js konvertiert in ISO 8601) oder bereits ein gültiger ISO-String – nicht `\"undefined\"` oder fehlend.\n- `/admin`, `/api` und `/dashboard` stehen in der Disallow-Liste.\n- Die `Sitemap:`-Direktive in `robots.txt` verwendet eine absolute URL.\n- Dynamische Routen (Blogbeiträge) werden über eine DB-Abfrage eingeschlossen, nicht nur statische Routen.\n- Die Sitemap enthält keine 404-, Weiterleitungs- oder Noindex-Seiten.\n- `bun run build` und dann `curl /sitemap.xml` gibt gültiges XML zurück (mit `xmllint` überprüfen).\n\n## 5. Testbefehle\n\n```bash\nbun run build && bun run start\n# or for Astro:\nbun run build && bun run preview\n\n# Validate sitemap XML\ncurl -s http://localhost:3000/sitemap.xml | xmllint --format - | head -40\n\n# Confirm robots.txt\ncurl http://localhost:3000/robots.txt\n\n# Confirm admin is disallowed and sitemap directive is present\ngrep -E \"Disallow|Sitemap\" <(curl -s http://localhost:3000/robots.txt)\n\n# Google Rich Results / URL Inspection simulation\ncurl -A \"Googlebot\" http://localhost:3000/sitemap.xml -I\n```\n\n## 6. Häufige Fehler\n\n- **`lastModified` ist `\"undefined\"`** – das `updatedAt`-Feld des Beitrags ist null. Absicherung: `lastModified: post.updatedAt ?? post.createdAt ?? new Date()`.\n- **Sitemap gibt 404 zurück** – `src/app/sitemap.ts` fehlt oder befindet sich außerhalb des `app`-Verzeichnisses.\n- **Alle Routen disallowed** – der Agent fügt versehentlich `Disallow: /` hinzu. Bestätigen Sie, dass nur Admin-/API-Pfade blockiert sind.\n- **`Sitemap:`-Direktive mit relativer URL** – Google ignoriert sie. Muss absolut sein: `https://example.com/sitemap.xml`.\n- **Nur statische Sitemap** – der Agent codiert Blog-Slugs fest, anstatt die Datenbank abzufragen. Bestätigen Sie, dass die Sitemap-Funktion `async` ist und echte Daten abruft.\n\n## 7. Korrektureingabeaufforderung\n\n```txt title=\"Fix Prompt\"\nThe sitemap.xml at /sitemap.xml includes every blog post with\nlastModified \"undefined\" (rendered as the string).\n\nFix in src/app/sitemap.ts:\n  const posts = await getBlogPosts();\n  return posts.map((post) => ({\n    url: `${BASE_URL}/blog/${post.slug}`,\n    lastModified: post.updatedAt ?? post.createdAt ?? new Date(),\n    changeFrequency: \"weekly\",\n    priority: 0.8,\n  }));\n\nEnsure getBlogPosts() returns rows that include updatedAt and createdAt.\n```\n\n## 8. PR-Beschreibung\n\n```md title=\"PR description\"\n## SEO: Add dynamic sitemap.xml and robots.txt\n\n**Next.js**: `src/app/sitemap.ts` + `src/app/robots.ts` using built-in\n`MetadataRoute` types. Sitemap includes static routes + all published blog\nposts with correct `lastModified` timestamps from the DB.\n\n**robots.txt** disallows `/admin`, `/api`, `/dashboard`; includes absolute\n`Sitemap:` directive pointing to the generated `/sitemap.xml`.\n\nBase URL read from `NEXT_PUBLIC_APP_URL` — no localhost URLs in production.\n```"
}