Journal / Entwicklung von Webprodukten
Wie der Cache in Next.js v13+ mit dem App Router funktioniert

Ein ausführlicher Überblick über die Funktionsweise des Caches in Next.js 13+, welche Cache-Arten es gibt und wie sie sich auf Performance und SEO auswirken.
Was ist ein Cache und wie funktioniert er in Next.js
Ein Cache speichert die Ergebnisse von Berechnungen oder Anfragen, sodass sie nicht wiederholt werden müssen – das beschleunigt die Anwendung. In Next.js können HTML-Seiten, Daten, Anfragen, das Rendering von Komponenten und sogar ganze Routen im Cache abgelegt werden.
Mit dem App Router (ab Next.js 13) hat sich die Philosophie geändert: Der Cache ist jetzt standardmäßig aktiv, und es liegt beim Entwickler, ihn abzuschalten, wenn dynamisches Verhalten gebraucht wird.
Die Richtlinie von Next.js lautet:
„Wir cachen alles, sofern nichts anderes angegeben ist"
Wenn Sie nicht ausdrücklich cache: 'no-store', next: { revalidate } oder dynamic = 'force-dynamic' angeben, dann gilt:
fetch()wird im Cache abgelegt- Layout und Seite können ebenfalls gecacht werden
- Routen werden dem Routen-Cache hinzugefügt
- das Rendering der Seite geht in den vollständigen Routen-Cache
Das sorgt für Performance, kann aber zu veralteten Daten und Problemen beim Debugging führen.
Cache-Arten in Next.js

Quelle: Next.js-Dokumentation
Full Route Cache (serverseitiger Routen-Cache)
Wird mit ISR (Incremental Static Regeneration) verwendet.
- Die gesamte Seite wird gecacht (HTML, Daten aus
layout.tsx,page.tsx,fetch(),headers(),cookies()). - Der Cache liegt auf dem Server.
- Funktioniert standardmäßig, wenn es kein dynamisches Verhalten gibt.
- Wird über
revalidateoderrevalidateTag()deaktiviert.
Am besten für Blogs, Marketingseiten und andere selten wechselnde Inhalte.
Beispiel:
export const revalidate = 60; // regenerate the page every 60 seconds
Data Cache (serverseitiger Cache für Datenabrufe)
- Antworten von
fetch()in Server Components werden gecacht. - Gesteuert über
next: { revalidate }odercache: 'no-store'.
Beispiel:
await fetch('https://api.com/posts', {
next: { revalidate: 300 },
});
Request Memoization (Cache pro Anfrage, serverseitig)
Das ist eine interne Optimierung von Next.js: Dasselbe fetch() oder dieselbe Funktion mit denselben Argumenten wird pro Rendering nur einmal ausgeführt.
- Innerhalb eines Server-Renderings verfolgt Next.js identische Anfragen.
- Wurden sie bereits ausgeführt, kommt das Ergebnis aus dem Cache.
- Funktioniert unabhängig von
cache: 'force-cache'oderno-store.
Gut für:
- DRY (don't repeat yourself) beim Abrufen derselben Daten in verschiedenen Komponenten.
- Bessere Performance ohne manuelle Optimierung.
- Wenn dasselbe
fetchsowohl inlayout.tsxals auch inpage.tsxverwendet wird.
Nicht für:
- Wiederverwendung über verschiedene Anfragen oder Nutzer hinweg (es ist kein globaler Cache).
- Das Umgehen des Data Cache – er arbeitet auf Basis der Request Memoization.
// lib/getUser.ts
export async function getUser() {
return fetch('https://api.com/user').then(res => res.json());
}
// app/layout.tsx
export default async function Layout({ children }) {
const user = await getUser(); // Call 1
return (
<div>
<Sidebar user={user} />
{children}
</div>
);
}
// app/page.tsx
import { getUser } from '../lib/getUser';
export default async function Page() {
const user = await getUser(); // Call 2, but actually not repeated
return <p>Hello, {user.name}</p>;
}
Physisch wird fetch nur einmal aufgerufen, und das Ergebnis wird an beiden Stellen verwendet.
Router Cache (clientseitiger Cache)
Das ist der Cache, den Sie selbst mit Bibliotheken wie SWR, React Query und anderen steuern.
- Funktioniert nur bei clientseitiger Navigation (
<Link>,router.push()). - Next.js kontrolliert diesen Cache nicht – Sie entscheiden, wie er sich etwas merkt.
- Sehr schnell, weil nichts neu gerendert wird – React hängt einfach aus dem Speicher ein.
- Aber er existiert nur, bis der Nutzer die Seite neu lädt (F5).
Am besten für interaktive Komponenten, Formulare, Dashboards.
Beispiel:
'use client';
import useSWR from 'swr';
const { data } = useSWR('/api/data', fetcher);
Visueller Vergleich der Cache-Arten
| Cache-Art | Wo er wirkt | Steuerung | Beispiel |
|---|---|---|---|
| Full Route Cache | Server (ganze Route) | revalidate, tag | export const revalidate = 60 |
| Data Cache | Server (Datenabruf) | cache, revalidate | fetch(..., { next: ... }) |
| Statisch generierter Cache | Server (Build-Zeit) | Keine | JSX ohne Daten |
| Clientseitiger Cache | Browser / Client | Sie selbst | SWR / React Query |
Ein Beispiel aus dem Alltag
Angenommen, Sie bauen einen Shop:
| Seite | Was zwischengespeichert wird | Welche Cache-Art verwenden |
|---|---|---|
/produkty | Produktliste, Aktualisierung alle 5 Minuten | Full Route Cache (ISR) |
/api/produkty | Produkt-API-Daten | Data Cache (mit revalidateTag) |
| SPA-Navigation | Schnelle Navigation | Router Cache (clientseitiger Cache) |
Typische Cache-Probleme im App Router
Daten aktualisiert – aber die Seite zeigt alte Daten
fetch() verwendet standardmäßig force-cache, wenn Sie nicht cache: 'no-store' oder next: { revalidate } angeben. Folglich merkt sich Next.js die Daten und aktualisiert sie nie.
Lösung:
- Fügen Sie
cache: 'no-store'hinzu, um das Caching auszuschalten - Oder
next: { revalidate: 60 }für ISR
fetch('https://api.com/data', { next: { revalidate: 60 } });
Die URL (searchParams) hat sich geändert, aber die Daten sind dieselben
Next.js führt die Abfrage nicht erneut aus, wenn die URL gleich ist. Verwenden Sie Parameter, fügen Sie sie direkt in die Abruf-URL ein:
fetch(`https://api.com/posts?category=${searchParams.category}`, {
next: { revalidate: 60 }
});
Navigation über <Link>, aber die Daten haben sich nicht aktualisiert
Es wurde der Router Cache (clientseitiger Cache) verwendet. Next.js sendet beim Zurückkehren zur Seite keine neue Anfrage.
Lösung:
- Erzwingen Sie die Aktualisierung der Daten mit
router.refresh() - Verwenden Sie
cache: 'no-store', wenn die Daten immer aktuell sein müssen
revalidatePath() oder revalidateTag() funktioniert nicht
- Der Cache wurde nicht angelegt (z. B. wegen
no-store) - Sie verwenden keine
tagsbeim Abruf - Sie rufen
revalidatePath()außerhalb einer Server Action auf
Lösung:
- Stellen Sie sicher, dass der Abruf
next: { tags: [...] }verwendet - Rufen Sie
revalidateTag('tag')in einer Server-Funktion auf
Das Layout ist gecacht und aktualisiert sich nicht
layout.tsx wird ebenfalls als RSC mit revalidate gecacht, und wenn die Daten darin gecacht sind, werden sie erst mit der nächsten ISR aktualisiert.
Lösung:
- Verwenden Sie
cache: 'no-store'für Abrufe in layout.tsx, wenn dort beispielsweise das Nutzerprofil steckt - Oder verlagern Sie den Abruf aus dem Layout in die Seite (oder in eine Client-Komponente mit SWR)
Unerwartetes Verhalten im Entwicklungsmodus (dev)
In next dev sind viele Caches abgeschaltet oder arbeiten anders, und revalidate funktioniert im Entwicklungsmodus nicht zuverlässig.
Tipp:
Testen Sie den Cache mit next build && next start oder in der Vorschau von Vercel.
Die Kombination aus cookies(), headers(), params und fetch() zerstört den Cache.
Wenn Sie dynamische Dinge verwenden wie:
cookies()
headers()
useSearchParams()
generateMetadata() mit dynamischen Werten
schaltet Next.js den Seiten-Cache automatisch ab und wechselt zu dynamic = 'force-dynamic'.
Checkliste: so vermeiden Sie Cache-Probleme
Legen Sie das Abrufverhalten immer ausdrücklich fest:
- Vergessen Sie nicht, Query-Parameter direkt in die Abruf-URL aufzunehmen
// no cache at all
fetch(..., { cache: 'no-store' })
// ISR (revalidate every 60 seconds)
fetch(..., { next: { revalidate: 60 } })
// manual invalidation via tag
fetch(..., { next: { tags: ['products'] } })
- Vergessen Sie nicht, Query-Parameter direkt in die URL zu legen, die an fetch übergeben wird
- Testen Sie mit
next build && next start– sonst wiegen Sie sich womöglich in falscher Sicherheit - Müssen Sie Daten manuell invalidieren, verwenden Sie
revalidatePath()oderrevalidateTag()
Läuft der Cache in Ihrem App-Router-Projekt bereits in Produktion und macht Probleme, genau solche Aufgaben übernehme ich als freiberuflicher Next.js-Entwickler: Cache-Audits, Korrekturen und Migrationen auf den App Router.