to mechanizm Next.js,
który łączy zalety stron statycznych (szybkość, cache CDN) z możliwością
aktualizacji treści bez pełnej przebudowy aplikacji.
W tradycyjnym podejściu statycznym (SSG) każda zmiana treści wymaga ponownego budowania całego serwisu, co w przypadku setek czy tysięcy stron oznacza minuty oczekiwania. ISR rozwiązuje ten problem na żądanie, pozwalając odświeżać pojedyncze strony w tle.
Dwa tryby rewalidacji
Rewalidacja czasowa (time-based)
Najprostszy tryb, czyli strona jest serwowana z pamięci podręcznej przez określony czas, po czym Next.js regeneruje ją w tle przy następnym żądaniu.
Podczas budowania — strona jest generowana i zapisywana w pamięci podręcznej
Przez 3600 sekund — każde żądanie dostaje wersję z pamięci podręcznej (natychmiast)
Po 3600 sekundach — pierwsze żądanie nadal dostaje starą wersję, ale w tle Next.js generuje nową
Następne żądania — dostają już zaktualizowaną wersję
Problem z rewalidacją czasową: nie masz kontroli nad momentem aktualizacji. Jeśli zmienisz treść w CMS, użytkownicy mogą widzieć starą wersję przez cały czas rewalidacji.
Rewalidacja na żądanie
Rewalidacja na żądanie pozwala wymusić regenerację strony w dokładnym momencie, gdy zmienią się dane. Zamiast czekać — natychmiast unieważniasz pamięć podręczną.
Next.js App Router oferuje dwa mechanizmy:
revalidatePath() — unieważnianie po ścieżce
Code
// app/api/revalidate/route.tsimport { revalidatePath } from 'next/cache'import { NextRequest, NextResponse } from 'next/server'export async function POST(request: NextRequest) { const { path, secret } = await request.json() // Weryfikacja tokena — bez tego każdy może rewalidować if (secret !== process.env.REVALIDATION_SECRET) { return NextResponse.json({ error: 'Nieprawidłowy token' }, { status: 401 }) } try { revalidatePath(path) return NextResponse.json({ revalidated: true, path }) } catch (error) { return NextResponse.json({ error: 'Rewalidacja nieudana' }, { status: 500 }) }}
// Rewalidacja konkretnej stronyrevalidatePath('/blog/moj-post')// Rewalidacja wszystkich stron w segmencierevalidatePath('/blog', 'page')// Rewalidacja layoutu i wszystkich stron pod nimrevalidatePath('/blog', 'layout')// Rewalidacja całej aplikacjirevalidatePath('/', 'layout')
revalidateTag() — unieważnianie po tagu
Tagi pozwalają grupować dane i rewalidować wszystkie strony, które z nich korzystają — jednym wywołaniem.
// app/api/revalidate-tag/route.tsimport { revalidateTag } from 'next/cache'import { NextRequest, NextResponse } from 'next/server'export async function POST(request: NextRequest) { const { tag, secret } = await request.json() if (secret !== process.env.REVALIDATION_SECRET) { return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }) } // W Next.js 16 revalidateTag wymaga drugiego argumentu (profilu). // 'max' daje semantykę stale-while-revalidate. revalidateTag(tag, 'max') return NextResponse.json({ revalidated: true, tag })}
Przykłady tagowania:
Code
// Tag per zasób — rewalidacja jednego postafetch(url, { next: { tags: ['post-nextjs-15'] } })revalidateTag('post-nextjs-15', 'max')// Tag per typ — rewalidacja wszystkich postówfetch(url, { next: { tags: ['all-posts'] } })revalidateTag('all-posts', 'max')// Tag per autor — rewalidacja postów jednego autorafetch(url, { next: { tags: ['author-maciej'] } })revalidateTag('author-maciej', 'max')
Integracja z CMS — webhooks
ISR na żądanie ma największy sens wtedy, gdy łączysz go z CMS-em. Po publikacji, edycji albo usunięciu treści CMS wysyła webhook do aplikacji, a endpoint API w Next.js unieważnia tylko te ścieżki lub tagi, których dotyczyła zmiana.
Przykład: webhook z headless CMS
Code
// app/api/cms-webhook/route.tsimport { createHmac } from 'crypto'import { revalidateTag, revalidatePath } from 'next/cache'import { NextRequest, NextResponse } from 'next/server'export async function POST(request: NextRequest) { // Weryfikacja podpisu webhooka const signature = request.headers.get('x-webhook-signature') const body = await request.text() if (!verifySignature(body, signature)) { return NextResponse.json({ error: 'Invalid signature' }, { status: 401 }) } const payload = JSON.parse(body) switch (payload.event) { case 'entry.publish': case 'entry.update': // Rewalidacja konkretnego wpisu revalidateTag(`post-${payload.entry.slug}`, 'max') // Rewalidacja strony głównej i listy bloga revalidatePath('/') revalidatePath('/blog') break case 'entry.delete': revalidatePath('/blog') revalidatePath('/') break case 'media.upload': // Nowe media — rewalidacja galerii revalidateTag('gallery', 'max') break } return NextResponse.json({ revalidated: true })}function verifySignature(body: string, signature: string | null): boolean { if (!signature) return false const expected = createHmac('sha256', process.env.WEBHOOK_SECRET!) .update(body) .digest('hex') return signature === expected}
Konfiguracja w popularnych CMS-ach
Strapi — utwórz webhook dla publikacji, aktualizacji i usunięcia wpisu. W payloadzie potrzebujesz co najmniej typu zdarzenia, typu treści i identyfikatora albo sluga wpisu, żeby endpoint Next.js wiedział, czy odświeżyć pojedynczą stronę, listę bloga, kategorię czy stronę główną.
Sanity — najlepiej użyć webhooka z filtrem na typ dokumentu, np. tylko post, caseStudy albo page. Dzięki temu edycja autora, assetu albo ustawień globalnych nie musi zawsze czyścić całego cache; możesz osobno obsłużyć wpis, listę wpisów i elementy wspólne, takie jak nawigacja.
Contentful — skonfiguruj webhook dla konkretnych Content Types i eventów publikacji. Dla artykułu zwykle rewalidujesz stronę wpisu oraz listę bloga; dla kategorii albo autora także strony archiwów, które pokazują powiązane wpisy.
WordPress headless — możesz użyć gotowego pluginu od webhooków albo własnej akcji save_post. Własny hook daje największą kontrolę, bo możesz pominąć rewizje, autozapisy i typy wpisów, które nie mają publicznej strony w Next.js:
W projektach z backendem w tym samym repozytorium (bez zewnętrznego CMS) Server Actions to naturalny sposób na mutowanie danych i natychmiastową rewalidację:
Code
// app/admin/posts/actions.ts'use server'import { revalidatePath, revalidateTag } from 'next/cache'import { db } from '@/lib/db'export async function updatePost(formData: FormData) { const slug = formData.get('slug') as string const title = formData.get('title') as string const content = formData.get('content') as string await db.post.update({ where: { slug }, data: { title, content, updatedAt: new Date() }, }) // Rewalidacja strony posta i listy bloga revalidateTag(`post-${slug}`, 'max') revalidatePath('/blog')}
Code
// app/admin/posts/[slug]/edit.tsximport { updatePost } from '../actions'export default function EditPost({ post }: { post: Post }) { return ( <form action={updatePost}> <input type="hidden" name="slug" value={post.slug} /> <input name="title" defaultValue={post.title} /> <textarea name="content" defaultValue={post.content} /> <button type="submit">Zapisz i opublikuj</button> </form> )}
Strategia tagowania dla dużych serwisów
Dla serwisów z setkami stron przyjmij stały schemat tagowania:
Tu trzeba być precyzyjnym, ponieważ Astro nie ma odpowiednika revalidatePath() i revalidateTag() na poziomie frameworka. Nie oznacza to, że Astro nie nadaje się do treści aktualizowanych z CMS-a, ale oznacza tylko, że problem rozwiązuje innym modelem.
W Next.js ISR jest częścią mechaniki frameworka i jego pamięci podręcznej, natomiast w Astro decyzję podejmujesz na poziomie sposobu renderowania oraz hostingu:
Potrzeba
Next.js
Astro
Blog lub strona firmowa, aktualizacja kilka razy dziennie
ISR albo zwykłe budowanie
SSG + webhook uruchamiający wdrożenie
Duży katalog, zmienia się pojedynczy produkt
revalidatePath() / revalidateTag()
SSR na żądanie + cache CDN albo platformowa invalidacja URL-i
Cena lub stan magazynowy musi być świeży
ISR z krótkim fallbackiem albo SSR
prerender = false, endpoint API albo Server Island
Treść statyczna, tylko jeden fragment dynamiczny
Cache Components / PPR
Server Islands
Tysiące stron programmatic SEO
ISR + tagi
zależy od częstotliwości zmian: SSG albo SSR z cache CDN
Model 1: Astro SSG i webhook do wdrożenia
Najprostszy model Astro to statyczne budowanie, czyli strona generuje HTML podczas wdrożenia, a potem CDN serwuje gotowe pliki. Jeśli edytor zmieni artykuł w CMS-ie, webhook uruchamia nowe budowanie.
To nie jest częściowa przebudowa (partial rebuild) w sensie Next.js ISR. To pełne wdrożenie, ale przy blogu, dokumentacji, stronie usługowej albo landing page'ach często jest to wystarczające i prostsze operacyjnie. Nie utrzymujesz tagów rewalidacji, endpointów API do rewalidacji ani serwera regenerującego strony.
Webhook z CMS-a nie uderza wtedy w /api/revalidate, tylko w hook wdrożeniowy platformy hostingowej. W praktyce wygląda to tak:
Code
CMS publish/update/delete -> webhook -> hook wdrożeniowy hostingu -> astro build -> nowa wersja statycznych plików na CDN
Ten model jest bardzo dobry, jeśli budowanie trwa kilkadziesiąt sekund albo kilka minut, a treść nie musi pojawić się w sekundę po kliknięciu „publish".
Model 2: Astro i renderowanie na żądanie
Jeśli dana trasa musi pobierać świeże dane przy żądaniu, Astro może renderować ją na żądanie. Projekt potrzebuje adaptera SSR, a trasa musi nie być prerenderowana:
To jest inny kompromis niż ISR. Nie przebudowujesz statycznej strony w tle, ale renderujesz odpowiedź w warstwie runtime. Dzięki temu dane mogą być świeże, ale płacisz kosztem żądania serwerowego oraz zależności od adaptera: Node, Vercel, Netlify, Cloudflare, Deno albo innej platformy wspierającej SSR.
Model 3: Astro SSR + cache CDN
Najbliżej modelu ISR jesteś wtedy, gdy Astro renderuje stronę na żądanie, a odpowiedź zapisujesz w cache CDN przez nagłówki HTTP. Astro nie musi mieć własnego revalidateTag(), jeśli CDN potrafi serwować starą odpowiedź i odświeżać ją w tle.
W tym wariancie świeżość zależy od hostingu i CDN. Jedna platforma pozwoli ręcznie wyczyścić konkretny URL, inna będzie honorować stale-while-revalidate, a jeszcze inna wymaga własnego endpointu API lub integracji z API pamięci podręcznej. Dlatego w Astro nie mówisz „robię ISR", tylko: robię renderowanie na żądanie i definiuję politykę pamięci podręcznej na poziomie infrastruktury.
Model 4: Server Islands zamiast przebudowy całej strony
Bardzo często nie trzeba odświeżać całej strony, ponieważ jeśli statyczny artykuł ma tylko jeden dynamiczny fragment (cenę, dostępność, rekomendacje albo licznik) Astro Server Islands pozwalają zostawić resztę jako statyczny HTML, a dynamiczny fragment wyrenderować osobno.
To nie jest ISR, ale rozwiązuje bardzo podobny problem, jakim jest przebudowywanie całej strony tylko dlatego, że jeden fragment musi być świeży. Dla Astro to bardziej naturalny wzorzec niż przenoszenie całej trasy w dynamiczną warstwę runtime.
Kiedy wybrać Next.js ISR, a kiedy Astro?
Jeśli potrzebujesz natywnej rewalidacji pojedynczych ścieżek i grup danych, Next.js wygrywa. revalidatePath(), revalidateTag() i Server Actions tworzą spójny model dla dużych katalogów, paneli administracyjnych, e-commerce i aplikacji, w których publikacja lub mutacja danych ma natychmiast odświeżać właściwe URL-e.
Astro jest idealne, gdy większość strony jest treścią i nie potrzebuje warstwy runtime. Blog, dokumentacja, baza wiedzy, strona usługowa albo landing page zwykle lepiej znoszą prosty model: statyczne budowanie, webhook po publikacji i bardzo lekki HTML. Gdy potrzebujesz świeżych fragmentów, sięgasz po renderowanie na żądanie, cache CDN albo selektywnie użyte Server Islands.
Audyt techniczny i optymalizacja pod kątem SEO i GEO.
Pełny ISR z rewalidacją na żądanie wymaga platformy obsługującej Next.js w trybie serwerowym — Vercel, Netlify, AWS Amplify, Coolify, samodzielny serwer Node.js. Statyczny eksport (output: export) nie wspiera ISR, bo nie ma serwera regenerującego strony.
Ile trwa regeneracja strony?
Regeneracja odbywa się w tle — trwa tyle, ile pobranie danych i wyrenderowanie strony (zwykle 0,5–5 s). Użytkownik, który wywołał rewalidację, widzi jeszcze starą wersję; następne żądanie dostaje już nową.
Czy mogę rewalidować tysiące stron naraz?
Tak, ale ostrożnie. revalidateTag(all-posts, max) unieważni pamięć podręczną wszystkich stron z tym tagiem. Regeneracja następuje leniwie — każda strona budowana jest na nowo dopiero przy pierwszym żądaniu po unieważnieniu, więc nie ma jednoczesnego skoku obciążenia.
Czy endpoint API do rewalidacji musi mieć tajny token?
Technicznie nie, ale to krytyczne dla bezpieczeństwa. Bez tokena (albo podpisu webhooka) każdy mógłby wymusić regenerację Twoich stron — przy masowych żądaniach to wektor ataku obciążającego serwer. Zawsze weryfikuj sekret albo podpis HMAC.
Jak zmieniła się rewalidacja tagów w Next.js 16?
revalidateTag wymaga teraz drugiego argumentu — profilu rewalidacji (np. max dla stale-while-revalidate). Forma jednoargumentowa jest przestarzała i daje błąd TypeScript. Dodatkowo doszedł updateTag() — wariant dla Server Actions z semantyką read-your-writes (natychmiastowy odczyt własnego zapisu).
Czy Astro ma odpowiednik ISR z Next.js?
Nie wprost. Astro nie ma frameworkowych funkcji revalidatePath() i revalidateTag(). Ten sam problem rozwiązuje inaczej: statyczne strony odświeżasz przez webhook uruchamiający wdrożenie, wybrane trasy możesz renderować na żądanie przez adapter i prerender = false, a przy SSR możesz oprzeć świeżość o Cache-Control oraz cache CDN danej platformy.
Kiedy Astro jest lepsze od ISR w Next.js?
Astro jest lepsze, gdy większość serwisu to treść, która może być statyczna: blog, dokumentacja, strona firmowa, landing page'e. Wtedy webhook z CMS może po prostu uruchamiać przebudowę i wdrożenie, a użytkownicy dostają bardzo lekki HTML. Next.js ISR wygrywa przy dużych katalogach, cenach, stanach magazynowych i treściach, które muszą aktualizować pojedyncze URL-e bez pełnego budowania.
O autorze
Maciej Sala
Maciej Sala — Product Manager i Frontend Developer z bogatym doświadczeniem w marketingu internetowym oraz SEO. Na co dzień pracuje z Reactem, Next.js i TypeScriptem, a ostatnio także z Astro i narzędziami do automatyzacji procesów AI. Sprawnie łączy perspektywę produktową z praktycznym podejściem do kodu. Przez kilka lat był związany z branżą gier wideo jako project manager i game designer. Absolwent historii na Uniwersytecie Jagiellońskim oraz studiów podyplomowych z marketingu internetowego na AGH w Krakowie. Po godzinach trenuje na siłowni, maluje figurki i rozwija własne projekty side-projecty.