
Next.js Sitemap generieren: Dynamische XML-Sitemaps für große Headless-Projekte

Die verlorene Schatzkarte: Warum Headless-Projekte oft unsichtbar sind
Dein Next.js-Frontend ist pfeilschnell, die Architektur ist entkoppelt und dein Laravel-Backend feuert strukturierte JSON-LD Daten in Lichtgeschwindigkeit aus. Du hast die perfekten 308-Redirects implementiert und Soft-404-Fehler ausgemerzt. Dein technologisches Fundament ist ein Meisterwerk.
Doch wenn du jetzt live gehst, wird ein entscheidendes Problem auftreten: Der Googlebot wird deine Website besuchen, die Startseite sehen, vielleicht ein paar Links im Menü verfolgen – und dann aufhören. Deine 10.000 tieferliegenden Produkte, die wertvollen alten Ratgeber-Artikel und die versteckten Landingpages bleiben für Wochen oder Monate unsichtbar.
Warum? Weil du dem Crawler im Headless-Zeitalter die Schatzkarte weggenommen hast.
Der Luxus der Monolithen
In der alten Welt der monolithischen Content-Management-Systeme (wie WordPress oder Typo3) war die Generierung einer sitemap.xml ein triviales Problem. Du hast ein SEO-Plugin installiert. Da das Plugin und dein Frontend physisch auf derselben Serverinstanz lagen und direkten Zugriff auf dieselbe MySQL-Datenbank hatten, konnte das Plugin bei jedem Speichern eines Artikels heimlich im Hintergrund eine riesige XML-Datei schreiben und auf dem Server ablegen.
Es war eine bequeme, aber stark gekoppelte Blackbox.
Der blinde Fleck im entkoppelten System
Wenn wir heute eine professionelle Headless-Architektur bauen, sind Backend und Frontend strikt voneinander getrennt. Unser Next.js-Frontend weiß absolut nichts über die Datenbank. Es kennt nur die URLs, die aktuell von Nutzern aufgerufen werden.
Wenn dein Redakteur im Headless-CMS einen neuen Beitrag veröffentlicht, existiert dieser Beitrag zwar in der API, aber Next.js erstellt nicht automatisch eine neue Route dafür (es sei denn, die URL wird explizit aufgerufen oder ein Webhook löst ein Rebuild aus). Noch kritischer: Es gibt kein magisches Plugin mehr, das dem Googlebot eine strukturierte Liste aller existierenden URLs überreicht.
Wir müssen die Verantwortung für die Indexierung selbst in die Hand nehmen. Wir müssen eine Sitemap bauen, die nicht starr als Datei auf dem Server liegt, sondern on the fly (dynamisch) generiert wird, sobald der Crawler danach fragt.
Und genau hier bietet der Next.js App Router eine unglaublich elegante, aber auch brandgefährliche Lösung: Die Datei sitemap.ts.

Die Magie der Datei sitemap.ts
Mit der Einführung des App Routers hat Next.js das Problem der Sitemap-Generierung auf brillante Weise standardisiert. Wir brauchen keine fragilen Drittanbieter-Pakete mehr, die nach dem Build-Prozess (postbuild) mühsam über unsere exportierten HTML-Dateien kriechen.
Next.js bietet uns stattdessen eine dedizierte Dateikonvention: Legst du eine Datei namens sitemap.ts (oder .js) in dein app/-Verzeichnis, behandelt Next.js diese Route beim Aufruf von /sitemap.xml automatisch als dynamischen XML-Endpunkt.
Das Prinzip ist atemberaubend einfach: Du exportierst eine asynchrone Funktion, fetchest deine Daten aus deiner Laravel-API (oder deinem CMS) und gibst ein schlichtes Array aus JavaScript-Objekten zurück. Next.js kümmert sich unter der Haube um die perfekte XML-Formatierung.
Lass uns einen Blick auf eine produktionsreife, dynamische Sitemap für unseren Shop werfen:
1// app/sitemap.ts
2
3import { MetadataRoute } from 'next';
4
5export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
6 const baseUrl = 'https://www.mein-shop.de';
7
8 // 1. Echte, dynamische Daten aus unserer Headless-API laden
9 const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/produkte`);
10 const produkte = await res.json();
11
12 // 2. Die Produktdaten auf das Next.js Sitemap-Format mappen
13 const produktUrls: MetadataRoute.Sitemap = produkte.map((produkt: any) => ({
14 url: `${baseUrl}/produkte/${produkt.slug}`,
15 // WICHTIG: Google liebt korrekte lastModified-Timestamps!
16 lastModified: new Date(produkt.updated_at),
17 changeFrequency: 'weekly',
18 priority: 0.8,
19 }));
20
21 // 3. Statische Routen und dynamische Routen vereinen
22 return [
23 {
24 url: baseUrl,
25 lastModified: new Date(),
26 changeFrequency: 'daily',
27 priority: 1.0,
28 },
29 {
30 url: `${baseUrl}/ratgeber`,
31 lastModified: new Date(),
32 changeFrequency: 'weekly',
33 priority: 0.9,
34 },
35 ...produktUrls, // Unsere hunderte Produkte dynamisch anhängen
36 ];
37}Warum dieser Ansatz den Monolithen schlägt
Überleg einmal, was wir hier gerade gebaut haben. Jedes Mal, wenn der Googlebot die URL https://www.mein-shop.de/sitemap.xml aufruft, wird dieser Code auf unserem Next.js-Server ausgeführt.
Die API wird abgefragt, die neuesten Produkte werden ausgelesen, das Array wird gebaut, als XML formatiert und direkt an den Crawler gestreamt. Wenn ein Redakteur im Backend den Preis ändert oder ein neues Produkt anlegt, ist die URL in der exakt gleichen Sekunde in der Sitemap für Google sichtbar. Keine Verzögerung. Keine Cronjobs. Keine abgelaufenen Caches, wenn wir es nicht wollen.
Wir haben die absolute Kontrolle.
Die tickende Zeitbombe: Das 50.000 URL-Limit
Dieser Code ist großartig für eine kleine bis mittelgroße Website. Aber was passiert in echten Enterprise-Szenarien? Was, wenn du 40.000, 80.000 oder gar eine halbe Million URLs verwaltest (wie das Vergleichsportal aus Artikel 3)?
Wenn du versuchst, in der sitemap.ts eine halbe Million Datensätze mit einem einzigen fetch() aus deiner Datenbank zu laden und in ein einziges Array zu stopfen, passieren drei katastrophale Dinge:
Dein Laravel-Backend bricht wegen Out-of-Memory-Fehlern zusammen.
Der Next.js Route-Handler überschreitet das Zeitlimit (Timeout auf Vercel z.B. 10 bis 60 Sekunden) und wirft einen 504-Fehler.
Google verweigert die Annahme der Datei, da das offizielle Sitemap-Protokoll strikt vorschreibt: Eine Sitemap darf maximal 50.000 URLs enthalten und nicht größer als 50 MB (unkomprimiert) sein.
Wenn deine Architektur wächst, wird deine einfache sitemap.ts zu einer tödlichen Falle. Wir müssen unser Denken ändern und in die Welt der Sitemap-Indizes und Paginierung eintreten.
Wie wir riesige Datenmengen elegant stückeln und dem Crawler häppchenweise servieren, klären wir im nächsten Teil.

Teile und Herrsche: Index-Sitemaps und Paginierung
Erinnern wir uns an die eiserne Regel des Googlebots: Eine Sitemap darf niemals mehr als 50.000 URLs enthalten. Wenn du einen großen E-Commerce-Shop oder ein umfangreiches Content-Portal mit 150.000 Seiten betreibst, wird Google eine einzige gigantische Datei schlichtweg ablehnen.
Die Lösung aus der SEO-Trickkiste heißt Index-Sitemap.
Stell dir eine Index-Sitemap wie das Inhaltsverzeichnis eines sehr dicken Buches vor. Anstatt alle 150.000 URLs aufzulisten, sagt die Index-Sitemap dem Googlebot: "Hey Crawler, ich habe zu viele URLs für eine Datei. Bitte schau dir stattdessen sitemap-0.xml, sitemap-1.xml und sitemap-2.xml an." Jede dieser untergeordneten Sitemaps enthält dann exakt 50.000 URLs.
In älteren Frameworks war das Erstellen einer solchen verschachtelten Struktur ein absoluter Albtraum. Im Next.js App Router ist es ein Meisterwerk der Eleganz, dank der Funktion generateSitemaps.
Die dynamische Paginierung mit generateSitemaps
Um unser 50.000-URL-Problem zu lösen, exportieren wir in unserer sitemap.ts nun zwei Funktionen.
Zuerst nutzen wir generateSitemaps, um Next.js mitzuteilen, wie viele Sitemaps wir überhaupt brauchen. Danach greift die eigentliche sitemap-Funktion diese generierte ID auf und nutzt sie, um bei unserer Laravel-API gezielt (paginiert) nur diesen einen Block an Daten anzufragen.
Schauen wir uns diesen Enterprise-Code an:
1// app/produkte/sitemap.ts
2
3import { MetadataRoute } from 'next';
4
5// 1. Die Index-Sitemap generieren
6export async function generateSitemaps() {
7 // Wir fragen unsere API: Wie viele Produkte haben wir insgesamt?
8 const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/produkte/count`);
9 const { totalProducts } = await res.json(); // z.B. 150.000
10
11 const limit = 50000;
12 // Berechnet die Anzahl der benötigten Sitemaps (150.000 / 50.000 = 3)
13 const totalPages = Math.ceil(totalProducts / limit);
14
15 // Next.js generiert nun automatisch eine sitemap-index.xml!
16 // Wir geben ein Array zurück: [{ id: 0 }, { id: 1 }, { id: 2 }]
17 return Array.from({ length: totalPages }).map((_, i) => ({
18 id: i.toString(),
19 }));
20}
21
22// 2. Die untergeordnete Sitemap generieren (wird für jede ID einmal aufgerufen)
23export default async function sitemap({ id }: { id: string }): Promise<MetadataRoute.Sitemap> {
24 const limit = 50000;
25 // Wenn id = '1' ist, überspringen wir die ersten 50.000 (Offset)
26 const offset = parseInt(id) * limit;
27
28 // Der GAMECHANGER: Wir laden nicht mehr alle Daten, sondern nur den aktuellen Chunk!
29 const res = await fetch(
30 `${process.env.NEXT_PUBLIC_API_URL}/produkte?limit=${limit}&offset=${offset}`
31 );
32 const produkte = await res.json();
33
34 // Mappen der Daten auf das korrekte XML-Format
35 return produkte.map((produkt: any) => ({
36 url: `https://www.mein-shop.de/produkte/${produkt.slug}`,
37 lastModified: new Date(produkt.updated_at),
38 changeFrequency: 'weekly',
39 priority: 0.8,
40 }));
41}Wenn du diesen Code deployst, passiert echte Headless-Magie.
Next.js generiert vollautomatisch die Route /produkte/sitemap.xml. Wenn der Googlebot diese aufruft, sieht er keinen Content, sondern einen sauberen Index, der auf /produkte/sitemap/0.xml, /produkte/sitemap/1.xml usw. verweist.
Besucht der Bot nun /produkte/sitemap/1.xml, nimmt Next.js die ID 1, reicht sie in deine sitemap-Funktion, berechnet den Offset und holt exakt die Produkte 50.001 bis 100.000 aus deiner Laravel-API.
Dein Node.js-Speicher (RAM) läuft nicht mehr voll. Du rennst in keine Timeouts mehr. Und Google bekommt perfekt proportionierte, leicht verdauliche 50MB-Häppchen.
Wir haben das Architektur-Problem gelöst. Doch wir haben dabei unbeabsichtigt ein noch viel gefährlicheres Performance-Monster erschaffen.
Stell dir vor, der Googlebot, der Bingbot und verschiedene andere SEO-Tools crawlen deine neuen, gesplitteten Sitemaps gleichzeitig. Bei jedem einzelnen Aufruf muss dein Next.js-Server die Laravel-API kontaktieren, die wiederum eine schwere LIMIT/OFFSET-Datenbankabfrage für 50.000 Produkte ausführen muss.
Wenn du diesen Endpunkt ungeschützt lässt, wird der Googlebot deine eigene Datenbank bei seinem nächsten großen Crawl mit einem Denial-of-Service (DoS) Angriff in die Knie zwingen.
Wir müssen die Sitemap cachen. Und das ist im Next.js App Router extrem trickreich.

Das Performance-Monster zähmen: Sitemap-Caching im App Router
Wir haben im letzten Abschnitt ein echtes architektonisches Kunstwerk geschaffen. Mithilfe von generateSitemaps haben wir die riesige Masse unserer URLs in mundgerechte, suchmaschinenkonforme 50.000er-Blöcke zerlegt und per Paginierung an den Googlebot übergeben. Damit läuft kein Server-Arbeitsspeicher mehr voll, und wir halten uns strikt an die offiziellen SEO-Spezifikationen.
Doch genau an diesem Punkt tappen viele Core-Entwickler in eine verheerende Performance-Falle, die ihr gesamtes System lahmlegen kann.
Überlege einmal, wie ein Suchmaschinen-Crawler operiert. Wenn der Googlebot deine Haupt-Sitemap (den Index) aufruft und dort drei Links zu den untergeordneten Sitemaps findet, liest er diese nicht nacheinander wie ein Mensch. Der Bot wirft sofort eine Armee von parallelen Crawl-Workers an. Er attackiert /sitemap/0.xml, /sitemap/1.xml und /sitemap/2.xml innerhalb derselben Millisekunde.
Wenn deine Sitemaps ungeschützt und ungecached sind, bedeutet das: Drei massive, rechenintensive API-Anfragen prallen zeitgleich auf dein Laravel-Backend. Deine Datenbank muss sofort drei gigantische LIMIT/OFFSET-Queries über hunderttausende Datensätze jagen.
Und jetzt multipliziere das mit dem Bingbot, dem Yandex-Bot, dem Applebot und den unzähligen SEO-Tools wie Ahrefs oder Semrush, die deine Seite täglich scannen. Ohne Caching wird deine Sitemap zum perfekten, hausgemachten Denial-of-Service (DoS) Angriff auf deine eigene Infrastruktur. Deine API bricht zusammen, deine Time to First Byte (TTFB) schießt in die Höhe, und Google bricht den Crawl frustriert ab.
Die Caching-Semantik im modernen Next.js
Seit Next.js 15 ist das automatische, aggressive Caching von Datenaustauschen standardmäßig deaktiviert. Ein normaler fetch()-Aufruf innerhalb einer dynamischen Umgebung gilt als "dynamisch bei Bedarf" (Dynamic on Demand). Das ist großartig für hochaktuelle Nutzerdaten, aber brandgefährlich für Sitemaps. Eine Sitemap muss nicht bei jedem Bot-Request das Backend bluten lassen. Es reicht völlig aus, wenn sie einmal am Tag oder alle paar Stunden frisch generiert wird.
Wie bringen wir Next.js also bei, dieses gewaltige XML-Dokument sicher zu puffern? Wir nutzen die mächtigen Route Segment Config Optionen. Wir zwingen Next.js dazu, die Sitemap wie eine statische Seite zu behandeln, die sich per Incremental Static Regeneration (ISR) vollautomatisch im Hintergrund aktualisiert.
Schauen wir uns an, wie wir unsere paginierte Sitemap mit nur einer einzigen Zeile Code in ein unbezwingbares Performance-Bollwerk verwandeln:
1// app/produkte/sitemap.ts
2
3import { MetadataRoute } from 'next';
4
5// DIE RETTUNG FÜR DEIN BACKEND:
6// Wir setzen die revalidate-Zeit auf 86400 Sekunden (exakt 24 Stunden).
7// Damit feuert Next.js die schweren Datenbank-Queries nur EINMAL am Tag ab!
8export const revalidate = 86400;
9
10export async function generateSitemaps() {
11 const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/produkte/count`, {
12 // Profi-Tipp: Auch den inneren Fetch-Call absichern!
13 next: { revalidate: 86400 }
14 });
15 const { totalProducts } = await res.json();
16 const limit = 50000;
17 const totalPages = Math.ceil(totalProducts / limit);
18
19 return Array.from({ length: totalPages }).map((_, i) => ({
20 id: i.toString(),
21 }));
22}
23
24export default async function sitemap({ id }: { id: string }): Promise<MetadataRoute.Sitemap> {
25 const limit = 50000;
26 const offset = parseInt(id) * limit;
27
28 // Dieser API-Call wird durch das globale 'revalidate' geschützt und im Vercel Data Cache abgelegt
29 const res = await fetch(
30 `${process.env.NEXT_PUBLIC_API_URL}/produkte?limit=${limit}&offset=${offset}`,
31 { next: { revalidate: 86400 } }
32 );
33 const produkte = await res.json();
34
35 return produkte.map((produkt: any) => ({
36 url: `https://www.mein-shop.de/produkte/${produkt.slug}`,
37 lastModified: new Date(produkt.updated_at),
38 changeFrequency: 'weekly',
39 priority: 0.8,
40 }));
41}Der Schutzschild steht: Was passiert unter der Haube?
Durch das Hinzufügen von export const revalidate = 86400; haben wir die Natur unserer Sitemap-Route radikal verändert.
Wenn du dein Projekt deployst oder baust, führt Next.js diesen Code einmal aus und speichert das fertige XML-Dokument im globalen CDN (Vercel Data Cache oder deinem eigenen Edge-Netzwerk).
Wenn nun eine Sekunde später der Googlebot anklopft und die Sitemap verlangt, passiert Folgendes:
Der Request wird sofort am Edge-Knoten abgefangen.
Next.js streamt das fertige XML-Dokument in unter 10 Millisekunden an den Bot zurück.
Dein eigentliches Laravel-Backend und deine SQL-Datenbank bekommen von diesem Request absolut gar nichts mit. Sie schlafen tief und fest weiter.
Erst wenn die 24 Stunden abgelaufen sind und ein neuer Request reinkommt, liefert Next.js nach dem Stale-While-Revalidate-Prinzip dem Bot noch einmal die alte Sitemap aus, stößt aber im Hintergrund flüsterleise eine einzige, frische Anfrage an dein Backend an, um den Cache für die nächsten 24 Stunden zu erneuern.
Damit hast du die absolute Skalierbarkeit erreicht. Egal, ob 5 oder 5.000 Bots deine Sitemap gleichzeitig scannen – dein System bleibt kühl, stabil und pfeilschnell.
Wir haben nun die Generierung, die Paginierung und das Caching gemeistert. Doch eine perfekte Sitemap nützt nichts, wenn Suchmaschinen gar nicht wissen, wo sie danach suchen sollen. Wir müssen das System abrunden, den Bots den Weg weisen und fehlerhafte URLs rigoros ausschließen.
Im fünften und letzten Teil dieses Artikels widmen wir uns den Best Practices für die robots.txt, dem Handling von unfertigen Entitäten und der finalen Validierung unserer XML-Schatzkarte.

Der Wächter des Labyrinths: robots.txt und Daten-Hygiene
Wir haben unsere Sitemap nun perfektioniert. Sie ist paginiert, sie respektiert das 50.000-URL-Limit und sie wird serverseitig so aggressiv gecacht, dass unsere Laravel-API vor Crawler-Attacken sicher ist.
Doch woher weiß der Googlebot überhaupt, dass diese Sitemap existiert? Suchmaschinen haben keine Glaskugel. Wenn sie eine neue Domain betreten, suchen sie als allererstes nach einer ganz bestimmten, uralten Textdatei: der robots.txt.
In der traditionellen Webentwicklung haben wir diese Datei händisch in den public-Ordner gelegt. Im Next.js App Router können wir das zwar immer noch tun, aber wir vergeben damit die Chance, dynamische Umgebungen (wie Staging vs. Production) sauber zu trennen.
Dynamische robots.ts im App Router
Genauso wie die sitemap.ts bietet Next.js eine dedizierte robots.ts (oder .js) Datei im Root-Verzeichnis deines app-Ordners an. Hier definieren wir, welche Crawler wir zulassen, welche Pfade tabu sind und – am wichtigsten – wo unsere neue Index-Sitemap liegt.
1// app/robots.ts
2
3import { MetadataRoute } from 'next';
4
5export default function robots(): MetadataRoute.Robots {
6 // Verhindere Indexierung auf Staging-Umgebungen!
7 const isProduction = process.env.NEXT_PUBLIC_ENVIRONMENT === 'production';
8 const baseUrl = 'https://www.mein-shop.de';
9
10 if (!isProduction) {
11 return {
12 rules: {
13 userAgent: '*',
14 disallow: '/', // Sperrt alle Bots komplett aus!
15 },
16 };
17 }
18
19 return {
20 rules: {
21 userAgent: '*',
22 allow: '/',
23 // Blockiere interne API-Routen oder Nutzer-Dashboards
24 disallow: ['/api/', '/dashboard/', '/checkout/'],
25 },
26 // HIER übergeben wir Google unsere dynamische Sitemap-Schatzkarte!
27 sitemap: `${baseUrl}/sitemap.xml`,
28 };
29}Wenn du diesen Code deployt hast, ist die Schleife geschlossen. Google besucht /robots.txt, liest die URL der Sitemap, ruft den Index auf, blättert durch die 50.000er-Chunks und indexiert deine Produkte aus dem blitzschnellen Vercel Data Cache.
Die goldene Regel der Daten-Hygiene
Bevor wir diesen Artikel abschließen, muss ich dich noch vor einem tückischen SEO-Fehler warnen, den fast jedes Entwicklerteam bei der ersten Headless-Sitemap macht: Blinder Vertrauensvorschuss an die API.
Wenn wir fetch('/produkte') in der sitemap.ts aufrufen, liefert die Datenbank oft alle Produkte. Aber was ist mit:
Produkten, die noch im Entwurfsstatus ("Draft") sind?
Artikeln, die ein Redakteur explizit auf
noindexgesetzt hat?Produkten, die dauerhaft aus dem Sortiment genommen wurden (404/301)?
Wenn du URLs in deine Sitemap packst, die beim Aufruf durch den Bot dann einen 404-Fehler werfen oder ein <meta name="robots" content="noindex"> Tag im Head haben, sendest du Google extrem widersprüchliche Signale. Die Sitemap sagt: "Indexier das, das ist wichtig!" Der HTML-Header sagt: "Geh weg, indexier mich nicht!"
Google straft solche Inkonsistenzen gnadenlos ab. Deine Sitemap verliert an "Trust" (Vertrauen), und im schlimmsten Fall ignoriert der Bot die Datei irgendwann komplett.
Deine Sitemap darf ausschließlich HTTP-200 URLs enthalten, die indexierbar (index, follow) und kanonisch (canonical) sind! Achte also penibel darauf, dass deine Laravel-API bereits vor dem Übertragen der Daten an Next.js alle Drafts, Noindex-Seiten und inaktiven Entitäten herausfiltert.

Zusammenfassung
Ein Headless-CMS in Kombination mit Next.js befreit dich von den Fesseln der Monolithen. Du kannst blitzschnelle, interaktive Frontends bauen, die Nutzer lieben.
Aber mit dieser Freiheit kommt die architektonische Verantwortung. Du musst die Navigationssysteme für Suchmaschinen selbst bauen. Du hast in diesem Artikel gelernt:
Wie du statische Sitemaps durch dynamische Code-Ausführung (
sitemap.ts) ersetzt.Wie du durch Index-Sitemaps und Paginierung (
generateSitemaps) das 50.000 URL-Limit knackst, ohne den Node.js-Speicher zu sprengen.Wie du deine Backend-API durch intelligentes ISR-Caching (
revalidate) vor Denial-of-Service-Attacken durch Crawler schützt.Wie du den Bot mit der robots.ts sicher durch dein Setup steuerst.
Deine Headless-Festung ist nun nicht nur pfeilschnell und perfekt strukturiert, sondern verfügt auch über ein fehlerfreies Leitsystem für Crawler. Deine Inhalte sind bereit, das Netz zu dominieren.
Teil der Serie
Headless SEO Mastery: Der Weg aus der Googlebot-Falle
Headless JavaScript SEO: Der ultimative Masterguide Pillar
JavaScript SEO im Headless-Zeitalter: Warum der Googlebot SPAs hasst (und wie du es fixst)
Laravel Headless SEO: So baust du das perfekte Datenmodell für deine API
Next.js Rendering SEO: Die ultimative Matrix für Google (CSR, SSR, SSG, ISR)
Next.js Metadaten SEO: Dynamische Title, OG-Images & Schema.org meistern
JSON-LD Headless: Schema.org programmatisch in verteilten Systemen meistern
Next.js Soft 404: Echtes Error-Handling und Redirects in Headless Apps
Next.js Sitemap generieren: Dynamische XML-Sitemaps für große Headless-Projekte
Häufig gestellte Fragen (FAQ)
In 95 % der Fälle lautet die Antwort: Nein. Mit der nativen sitemap.ts und generateSitemaps hat Next.js 14/15 die Notwendigkeit für externe Pakete (die oft den Build-Prozess verlangsamen) obsolet gemacht. Nur bei extrem exotischen Anforderungen (wie speziellen Video-Sitemaps) können externe Tools noch sinnvoll sein.
Ja! Die Dateikonvention erlaubt es dir, in jedem beliebigen Ordner eine sitemap.ts anzulegen. Du kannst eine /app/blog/sitemap.ts und eine /app/produkte/sitemap.ts erstellen. Um sie zu vereinen, referenzierst du sie einfach beide als Liste in deiner /app/robots.ts.
Für normale E-Commerce- oder Ratgeber-Seiten ist das meist nicht nötig, da Google Bilder durch das Crawlen des HTML-Contents selbst hervorragend findet. Betreibst du jedoch ein stark visuelles Portal (z.B. Stockfotos oder Kunst), solltest du die nativen Sitemap-Erweiterungen nutzen, um <image:image> Tags zu injizieren.
Obwohl du sie in der robots.txt verlinkt hast, solltest du sie aktiv in der Google Search Console unter dem Menüpunkt "Sitemaps" einreichen. Dort siehst du sofort, ob Google die Datei erfolgreich parsen konnte, wie viele URLs gefunden wurden und ob es Fehler (wie 404-Seiten in der XML) gab.
Ausblick auf Artikel 8: Dem Labyrinth entkommen – Crawl-Traps in SPAs vermeiden
Deine Sitemap steht wie eine Eins, und der Googlebot hält nun die perfekte Schatzkarte zu all deinen Inhalten in den Händen. Aber was passiert, wenn der Crawler durch die Vordertür deine Website betritt und sich sofort in einem endlosen, dynamischen Labyrinth verläuft?
In der Welt der Single Page Applications (SPAs) und Headless-Setups lauern unsichtbare Gefahren: Crawl-Traps. Ein einziges unschuldiges JavaScript-Event oder eine dynamische Filter-Navigation kann den Bot in eine Endlosschleife schicken und dein hart erarbeitetes Crawl-Budget innerhalb von Minuten restlos verbrennen.
Im kommenden Artikel 8 widmen wir uns der Architektur einer fehlerfreien internen Verlinkung und dem sauberen Routing:
Die
onClick-Falle: Warum Suchmaschinen keine Buttons klicken und wie du die Next.js<Link>-Komponente im Vergleich zu herkömmlichen<a>-Tags absolut SEO-konform einsetzt.Filter und Paginierung bändigen: Facetten-Suchen (z. B. nach Farbe, Größe oder Preis) können exponentiell Millionen von sinnlosen URLs generieren. Wir zeigen dir, wie du das verhinderst.
Parameter-Chaos und Canonicals: Wie du mit Canonical-Tags bei parametrisierten URLs (wie
?sort=price_asc) umgehst, um Duplicate Content zu vernichten und deinen Link-Juice zu bündeln.
Mach dich bereit, deine interne Verlinkung von einem undurchsichtigen JavaScript-Dschungel in eine saubere Hochgeschwindigkeitsstrecke für Suchmaschinen zu verwandeln!
Jetzt lesen: Artikel 8 – Crawl-Traps in SPAs vermeiden: Sauberes Routing und interne Verlinkung

Dietrich Bojko
Senior Webentwickler
Webinteger arbeitet seit vielen Jahren produktiv mit
Linux-basierten Entwicklungsumgebungen unter Windows.
Der Fokus liegt auf
performanten Setups mit WSL 2, Docker, PHP, Node.js und modernen
Build-Tools in realen Projekten –
nicht auf theoretischen Beispielkonfigurationen.
Die Artikel dieser Serie entstehen direkt aus dem täglichen Einsatz in Kunden- und Eigenprojekten und dokumentieren bewusst auch typische Fehler, Engpässe und bewährte Workarounds.


