Web razvoj
Next.jsCacheiranjePerformanseReactApp RouterSWR

Objašnjene strategije cacheiranja u Next.js-u: SSR, SSG, ISR, Route Cache i SWR

AO
Adrijan Omićević
·15 min čitanja

# Što ovaj vodič pokriva#

Moderno cacheiranje u Next.js-u nije jedan prekidač. U App Routeru tipično kombinirate više slojeva: način server renderiranja, server-side cacheve, CDN cacheiranje i client-side cacheiranje.

Ovaj vodič objašnjava kako Next.js strategije cacheiranja funkcioniraju u praksi, kada koristiti SSR, SSG, ISR, Route Cache i SWR, te kako izbjeći skupe greške poput posluživanja zastarjelih auth podataka ili podataka “preko tenant-a”.

Na kraju ćete imati tablice za donošenje odluka, production-ready isječke i mentalni model koji možete primijeniti na landing stranice, blogove, multi-tenant SaaS aplikacije i dashboarde.

# Moderni Next.js model cacheiranja u App Routeru#

U App Routeru, cacheiranje je oblikovano trima glavnim pitanjima:

  1. 1
    Kada se HTML proizvodi: u trenutku zahtjeva, u buildu ili prema rasporedu revalidacije.
  2. 2
    Gdje se rezultati pohranjuju: cache izlaza na serveru, cache podataka na serveru, CDN, browser i in-memory cache na klijentu.
  3. 3
    Što čini odgovor dinamičnim: cookies, headers, search params i eksplicitno dinamična konfiguracija.

U igri je više cacheva. Nazivi se razlikuju kroz dokumentaciju i verzije, ali ovi koncepti ostaju stabilni.

SlojŠto cacheiraTipična koristTipičan rizik
CDN / edge cacheCijele odgovore za javne straniceNajniža latencija globalnoPogrešno cacheiranje personaliziranih stranica
Route-level output cacheRenderirani output segmenta ruteBrz TTFB kod ponovnih posjetaZastarjeli HTML ako je krivo podešeno
Data cacheRezultate fetch i srodnih poziva podatakaManje opterećenje backendaCross-user ili cross-tenant curenje ako je krivo “scopirano”
Client cache (SWR)JSON odgovore i izvedeno UI stanjeTrenutni prijelazi i manje refetchanjaZastarjeli UI ako invalidacija nedostaje

ℹ️ Napomena: Kad timovi kažu “Next.js mi cacheira stranicu”, često misle na cacheirani render output ili cacheirane data fetch pozive. To nije isto, i debugiranje postaje puno lakše kad to razdvojite.

Ako su performanse primarni cilj, rano planirajte i mjerenje i zaštitne ograde. Cacheiranje uparite sa stvarnom observability infrastrukturom kako biste vidjeli hit rate cachea, učestalost revalidacije i opterećenje backenda prije i nakon promjena. Praktičan setup je obrađen u Vodič za observability web aplikacija: logovi, metrike, tracing.

# SSR, SSG, ISR: Što znače u Next.js-u 2026.#

Stari pojmovi iz Pages Routera i dalje pomažu, ali u App Routeru ih izražavate kroz caching direktive i ponašanje dinamičkog renderiranja.

SSR: Server-Side Rendering po zahtjevu#

Koristite SSR kada svaki zahtjev može legitimno proizvesti drugačiji rezultat, npr.:

  • dashboardi za prijavljene korisnike
  • admin ekrani specifični za tenant
  • cijene prikazane u korisnikovoj valuti na temelju profila
  • A/B testovi koji moraju biti konzistentni po korisniku

U App Routeru SSR često znači da se isključite iz cacheiranja koristeći no-store za podatke i osigurate da je ruta dinamična.

JavaScript
// app/dashboard/page.js
export const dynamic = 'force-dynamic';
 
export default async function DashboardPage() {
  const res = await fetch('https://api.example.com/me/summary', {
    cache: 'no-store',
    headers: { Authorization: `Bearer ${process.env.API_TOKEN}` },
  });
 
  const data = await res.json();
  return <pre>{JSON.stringify(data, null, 2)}</pre>;
}

Kada je SSR bitan: smanjenje zastarjelosti podataka često je važnije od sirove brzine. Ako optimizirate percipirane performanse za autentificirane ekrane, fokusirajte se na server streaming, smanjenje vremena odgovora backenda i pametno client-side cacheiranje za navigaciju unutar aplikacije. Za šire taktike tuninga performansi vidi Optimizacija performansi web stranice.

SSG: Static Site Generation u buildu#

Koristite SSG kada se sadržaj rijetko mijenja i može biti isti za sve, npr.:

  • marketing stranice
  • dokumentacija
  • blog postovi
  • javni katalozi proizvoda koji se rijetko ažuriraju

U App Routeru SSG se tipično dogodi kada Next.js može zaključiti da ruta nema dinamičku upotrebu i da se podaci mogu cacheirati.

Za poznate dinamičke segmente, parametre obično generirate u buildu.

JavaScript
// app/blog/[slug]/page.js
export async function generateStaticParams() {
  const res = await fetch('https://cms.example.com/posts', { cache: 'force-cache' });
  const posts = await res.json();
  return posts.map((p) => ({ slug: p.slug }));
}
 
export default async function BlogPostPage({ params }) {
  const res = await fetch(`https://cms.example.com/posts/${params.slug}`, {
    cache: 'force-cache',
  });
  const post = await res.json();
  return <article>{post.title}</article>;
}

SSG je bitan kada želite predvidljiv, brz TTFB i minimalan trošak runtime infrastrukture. Potpuno statična ruta može se posluživati s CDN-a uz odlične performanse, često i ispod 100 ms globalnog TTFB-a ovisno o regiji i CDN-u.

ISR: Incremental Static Regeneration putem revalidacije#

Koristite ISR kada je sadržaj uglavnom statičan, ali se mora ažurirati bez redeploya, npr.:

  • oglasi za posao
  • dostupnost zaliha
  • marketing stranice s čestim izmjenama teksta
  • javni dashboardi koji se osvježavaju svakih nekoliko minuta

U App Routeru ISR se izražava kroz prozor revalidacije. Next.js cacheira output i regenerira ga nakon što prozor istekne.

JavaScript
// app/jobs/page.js
export const revalidate = 300; // 5 minutes
 
export default async function JobsPage() {
  const res = await fetch('https://api.example.com/jobs', {
    next: { revalidate: 300 },
  });
  const jobs = await res.json();
  return <pre>{JSON.stringify(jobs, null, 2)}</pre>;
}

ISR je bitan jer dramatično smanjuje opterećenje backenda uz razumno svjež sadržaj. Revalidate od 5 minuta na stranici s puno prometa može smanjiti origin zahtjeve za više od 99 posto u odnosu na SSR, ako većina posjetitelja dobije cacheirani output.

⚠️ Upozorenje: ISR je opasan za sve što je specifično za korisnika. Ako HTML sadrži podatke izvedene iz cookies, sessiona, tenant headera ili per-user prava, možete cacheirati i poslužiti pogrešan sadržaj pogrešnom korisniku.

# Route Cache vs Data Cache: Dvije server-side komponente koje morate razdvojiti#

App Router potiče razmišljanje u segmentima rute i server komponentama. To je moćno, ali može zamagliti što se zapravo cacheira.

Route Cache: Cacheiranje renderiranog outputa#

Route Cache sprema renderirani output za segment rute. Ako je segment statičan ili revalidiran, sljedeći posjetitelj može dobiti brz HTML bez ponovnog računanja cijelog stabla.

Ovaj cache je posebno vrijedan za stranice gdje je React Server Component stablo veliko ili skupo za renderiranje.

Data Cache: Cacheiranje rezultata fetch poziva#

Data Cache sprema rezultate server-side zahtjeva za podacima, najčešće fetch. To smanjuje ponovljene pozive istom backendu za identične zahtjeve.

U praksi možete imati:

  • Cacheirana ruta, cacheirani podaci: najbolje performanse za javne stranice.
  • Dinamična ruta, cacheirani podaci: često za polu-dinamične stranice ili zajedničke referentne podatke.
  • Cacheirana ruta, dinamični podaci: moguće ako mali dinamični dio prisili re-render, ali se i dalje mogu ponovno koristiti cacheirani subtree-ovi ovisno o strukturi.
  • Dinamična ruta, bez cacheiranih podataka: ispravno za prikaze specifične za korisnika.

Jednostavan mentalni model:

Što se mijenja po korisniku?Route output cacheData cacheTipična strategija
NištaDaDaSSG s force-cache
Povremeno se mijenja za sveDa, uz revalidateDa, uz revalidateISR
Mijenja se po zahtjevu, ali isto za sve korisnikeObično nePonekad daSSR plus cacheirani referentni podaci
Mijenja se po korisniku ili tenant-uNeNe za “scopirane” endpointoveSSR no-store plus SWR

# Tablice odluke: Koju strategiju koristiti i kada#

Većina timova pada na cacheiranju jer odaberu tehniku, a ne politiku. Odlučujte prema svježini, personalizaciji i načinima na koje sustav može zakazati.

Tablica odluke prema tipu stranice#

Tip stranicePersonalizacijaPotreba za svježinomPreporučeni pristupNapomene
Marketing landing stranicaNemaNiskaSSGDodajte CDN caching headere ako je primjenjivo
Blog postNemaSrednjaISR revalidate 300 do 3600Dodajte on-demand revalidaciju na publish
Javni popis proizvodaNema ili minimalnaSrednjaISR plus tagged revalidateIzbjegavajte SSR ako je promet visok
Dashboard za prijavljene korisnikeVisokaVisokaSSR no-store plus SWRNikad ne cacheirajte HTML između korisnika
Multi-tenant adminVisokaVisokaSSR no-storeKoristite tenant-aware API i strogi auth
Rezultati pretrageNiska do srednjaSrednjaSSR s kratkotrajnim cacheiranjem ili client fetchCacheiranje ovisi o obrascima upita

Tablica odluke prema tipu podataka#

PodaciPrimjerMogućnost cacheiranjaPreporučeno cacheiranjeZašto
Referentni podacidržave, planovi, feature flagoviVisokaData cache s dugim revalidateRijetko se mijenja, dijeljeno među korisnicima
Javni sadržajblog post JSONVisokaData cache i ISRIsto za sve
Brojčano stanje zaliharazina zalihaSrednjakratki revalidate ili on-demand tagoviTreba svježinu, ali nije po korisniku
Auth/sessiontrenutni korisnik, permisijeNemano-storeNe smije nikad “procuriti”
Tenant-scoped konfiguracijabranding, limitiSrednja, ali scopedcache po tenant ključu ili no-storeKrivi cache key uzrokuje cross-tenant curenje

💡 Savjet: Ako ne možete jasno definirati siguran cache key za dio podataka, tretirajte ga kao necacheabilan i koristite no-store. Cacheiranje možete vratiti kasnije uz eksplicitno “scopiranje”.

# Praktični obrasci i kod za App Router#

Obrazac 1: Javne stranice sa SSG i cacheiranim podacima#

Cilj: najbrži mogući TTFB i minimalno opterećenje backenda.

JavaScript
// app/page.js
export const dynamic = 'force-static';
 
export default async function HomePage() {
  const res = await fetch('https://cms.example.com/home', {
    cache: 'force-cache',
  });
  const home = await res.json();
  return <main>{home.heroTitle}</main>;
}

Na što paziti: ako slučajno koristite cookies ili headers u ovoj ruti, Next.js će je tretirati kao dinamičnu i izgubit ćete statičko cacheiranje.

Obrazac 2: ISR za sadržaj koji se mijenja bez deploya#

Cilj: zadržati stranice brzim, uz izbjegavanje predugog zastarijevanja sadržaja.

JavaScript
// app/pricing/page.js
export const revalidate = 600;
 
export default async function PricingPage() {
  const res = await fetch('https://cms.example.com/pricing', {
    next: { revalidate: 600 },
  });
  const pricing = await res.json();
  return <pre>{JSON.stringify(pricing, null, 2)}</pre>;
}

Kad 10 minuta nije prihvatljivo, koristite on-demand revalidaciju s tagovima, opisanu u nastavku.

Obrazac 3: On-Demand revalidacija s tagovima#

Revalidacija po vremenu je gruba. Tagovi vam omogućuju invalidaciju samo onoga što se promijenilo.

Koristite tagove na fetchu:

JavaScript
// app/lib/cms.js
export async function getPost(slug) {
  const res = await fetch(`https://cms.example.com/posts/${slug}`, {
    next: { tags: ['post', `post:${slug}`] },
  });
  return res.json();
}

Zatim pokrenite revalidaciju iz webhook endpointa:

JavaScript
// app/api/revalidate/route.js
import { revalidateTag } from 'next/cache';
 
export async function POST(request) {
  const body = await request.json();
  const slug = body.slug;
 
  revalidateTag('post');
  revalidateTag(`post:${slug}`);
 
  return Response.json({ ok: true });
}

Ovaj obrazac je bitan za editorial workflowe. Ako vaš CMS objavljuje 50 postova dnevno, revalidacija temeljena na tagovima izbjegava regeneriranje nepovezanih stranica.

Obrazac 4: Autentificirani SSR bez zastarjelih korisničkih podataka#

Cilj: prvo ispravnost i sigurnost, zatim brzina.

Česti zahtjevi:

  • korisnički profil mora odražavati najnovije permisije
  • tenant kontekst mora biti točan
  • nema cross-user cacheiranja HTML-a
JavaScript
// app/account/page.js
import { cookies } from 'next/headers';
 
export const dynamic = 'force-dynamic';
 
export default async function AccountPage() {
  const token = cookies().get('session')?.value;
 
  const res = await fetch('https://api.example.com/me', {
    cache: 'no-store',
    headers: { Authorization: `Bearer ${token}` },
  });
 
  if (!res.ok) throw new Error('Failed to load profile');
  const me = await res.json();
 
  return <main>Signed in as {me.email}</main>;
}

Ovdje React Server Components mogu pomoći: privatne podatke možete zadržati na serveru, smanjiti client bundle size i svejedno brzo streamati UI. Ako trebate podsjetnik kako granica između server i client komponenti utječe na protok podataka, vidi Vodič za React Server Components.

# SWR: Client-side stale-while-revalidate koji nadopunjuje server cacheiranje#

Server cacheiranje poboljšava TTFB i smanjuje opterećenje origina. SWR poboljšava percipirane performanse unutar aplikacije, posebno nakon inicijalnog učitavanja.

Koristite SWR kada:

  • korisnici prelaze između tabova u dashboardu
  • želite odmah prikazati cacheirani UI, pa zatim revalidirati
  • trebate optimistična ažuriranja za forme i toggleove
  • trebate periodično osvježavanje dok ostajete na istom ekranu

Minimalni SWR setup#

JavaScript
// app/lib/fetcher.js
export async function fetcher(url) {
  const res = await fetch(url, { credentials: 'include' });
  if (!res.ok) throw new Error('Request failed');
  return res.json();
}
JavaScript
// app/dashboard/components/KpiCard.client.js
'use client';
 
import useSWR from 'swr';
import { fetcher } from '@/app/lib/fetcher';
 
export function KpiCard() {
  const { data, isLoading, error } = useSWR('/api/kpi', fetcher, {
    revalidateOnFocus: true,
    dedupingInterval: 10_000,
  });
 
  if (isLoading) return 'Loading...';
  if (error) return 'Failed to load';
  return `Revenue: ${data.revenue}`;
}

Zašto je bitno: deduping znači da više komponenti može tražiti isti ključ i SWR će izbjeći duple mrežne pozive unutar intervala. Za dashboard s 10 KPI kartica to može smanjiti “client chatter” za 50 do 90 posto, ovisno o tome kako strukturirate ključeve.

SWR invalidacija nakon mutacija#

Većina problema sa zastarjelim UI-jem nije uzrokovana cacheiranjem. Uzrok je nedostatak invalidacije nakon promjena.

JavaScript
// app/dashboard/components/Toggle.client.js
'use client';
 
import useSWR, { mutate } from 'swr';
import { fetcher } from '@/app/lib/fetcher';
 
export function Toggle() {
  const { data } = useSWR('/api/settings', fetcher);
 
  async function onToggle() {
    await fetch('/api/settings', { method: 'POST' });
    await mutate('/api/settings');
  }
 
  return <button onClick={onToggle}>Refresh settings</button>;
}

Ovaj obrazac je bitan kada je ispravnost važna, ali i dalje želite responzivan UI.

# Česte zamke u Next.js strategijama cacheiranja#

Ovo su problemi koje najčešće viđamo u auditima i produkcijskim incidentima.

Zamka 1: Cacheiranje autentificiranog HTML-a#

Ako ruta čita cookies, headers ili session state, a ipak završi cacheirana, možete “procuriti” korisničke podatke. To se može dogoditi zbog pogrešno primijenjenih revalidate postavki ili pretpostavke da će hosting platforma “sama ispravno odraditi”.

Mitigacija:

  • označite user-specific rute kao dinamične
  • koristite cache: 'no-store' za korisničke podatke
  • osigurajte da CDN-ovi ne cacheiraju autentificirane odgovore

Zamka 2: Cross-tenant curenje podataka zbog dijeljenog Data Cachea#

Multi-tenant aplikacije često prenose tenant kontekst kroz:

  • subdomenu
  • X-Tenant-Id header
  • cookie
  • JWT claim

Ako vaš cacheirani fetch ne varira po tom tenant kontekstu, jedan tenant može dobiti podatke drugog tenant-a.

Mitigacija:

  • uključite tenant identifikator dosljedno u URL zahtjeva ili u request headere
  • tretirajte tenant-scoped endpointove kao no-store osim ako možete garantirati ispravne cache ključeve
  • odvojite javne i privatne data pathove

🎯 Ključna poruka: U multi-tenant sustavima, “cacheable” nije svojstvo endpointa. To je svojstvo endpointa plus punog skupa inputa koji utječu na odgovor, posebno tenant i auth kontekst.

Zamka 3: Pretpostaviti da revalidate znači “uvijek svježe”#

Revalidacija nije jamstvo trenutne svježine. To je politika koja razmjenjuje svježinu za performanse.

Ako je poslovni zahtjev “mora se ažurirati unutar 10 sekundi”, revalidate od 5 minuta nije rješenje. Koristite on-demand revalidaciju ili SSR za taj dio stranice.

Zamka 4: Miješanje search parametara sa statičkim cacheiranjem#

Search stranice s query stringovima mogu “eksplodirati” kardinalitet cachea. Ako cacheirate svaki jedinstveni query, možete završiti s niskim hit rateom i velikim churnom pohrane.

Mitigacija:

  • SSR za rezultate pretrage
  • cacheirajte samo popularne upite
  • prebacite pretragu na client fetch sa SWR-om i server-side rate limitingom

Zamka 5: Debugiranje bez metrika#

Problemi s cacheiranjem izgledaju kao nasumična zastarjelost dok ne možete vidjeti:

  • cache hit ili miss po ruti
  • broj backend zahtjeva po page viewu
  • revalidation evente
  • starost odgovora (response age)

Ako ne mjerite, ili ćete previše cacheirati i narušiti ispravnost, ili ćete premalo cacheirati i propustiti dobitke u performansama. Uparite rad na performansama s Optimizacija performansi web stranice i instrumentacijom iz Vodič za observability web aplikacija: logovi, metrike, tracing.

# Praktični checklist: “Koji trebam koristiti?”#

Koristite ovaj checklist prije implementacije caching politike.

Korak 1: Klasificirajte stranicu#

  1. 1
    Je li ijedan dio user-specific ili tenant-specific
  2. 2
    Treba li odražavati promjene unutar minuta ili sekundi
  3. 3
    Kakav je promet: long-tail ili koncentriran
  4. 4
    Što se događa ako je sadržaj zastario

Korak 2: Odaberite bazni način renderiranja#

  • Ako je user-specific: SSR i no-store
  • Ako je javno i stabilno: SSG
  • Ako je javno i mijenja se: ISR s revalidate i po potrebi tagovi

Korak 3: Odlučite kako riješiti interaktivnost#

  • Ako je sadržaj interaktivan i mijenja se tijekom sessiona: SWR s invalidacijom nakon mutacija
  • Ako je sadržaj uglavnom read-only: više se oslonite na server cacheiranje

Korak 4: Dodajte sigurne putanje invalidacije#

  • Revalidacija temeljena na vremenu za jednostavne slučajeve
  • Revalidacija temeljena na tagovima za CMS i strukturirani sadržaj
  • Eksplicitni SWR mutate pozivi nakon ažuriranja

# Ključne poruke#

  • Cacheiranje tretirajte kao slojevito: route output cache, data cache, CDN cache i client SWR rješavaju različite probleme.
  • Koristite SSG za stvarno javne i stabilne stranice, ISR za javne stranice koje se mijenjaju bez redeploya, te SSR no-store za stranice u auth ili tenant opsegu.
  • U mentalnom modelu razdvojite Route Cache i Data Cache kako biste mogli razumno zaključiti što se zapravo ponovno koristi.
  • Preferirajte on-demand revalidaciju temeljenu na tagovima kada trebate brzu svježinu nakon CMS promjena bez regeneriranja svega.
  • Za dashboarde kombinirajte SSR ispravnost sa SWR-om za trenutne client-side prijelaze i eksplicitnu invalidaciju nakon mutacija.

# Zaključak#

Next.js strategije cacheiranja najefikasnije su kada krenete od ispravnosti i opsega podataka, a zatim dodate cacheiranje samo ondje gdje možete definirati sigurne ključeve i pravila invalidacije. Ako želite pomoć u dizajniranju caching politike za multi-tenant SaaS, auditiranju rizika zastarjelosti ili poboljšanju performansi bez lomljenja auth flowova, Samioda može pomoći.

Kontaktirajte nas kako bismo pregledali vaš Next.js App Router setup i isporučili caching strategiju koja je brza, svježa i sigurna.

FAQ

Share
A
Adrijan OmićevićSamioda Team
All articles →

Trebate pomoć s projektom?

Gradimo prilagođena rješenja koristeći tehnologije iz ovog članka. Senior tim, fiksne cijene.