7. modulRenderelési módok és routeRules
Nuxt for Devs · 7. modul

Renderelési módok és routeRules

A Nuxt egyik legerősebb képessége, hogy útvonalanként más renderelési stratégiát adhatsz — és itt van a tananyag legőszintébb szakasza is: az isr és az swr nem azt csinálja Workers-en, amit a dokumentációt olvasva várnál. Megnézzük, mi történik valójában, és mit állíts be, hogy tényleg működjön.

7.1A négy mód

MódMikor dől el a HTMLFut a Worker?Mire jó
Universal (SSR)
az alapértelmezés
Kérésenként, a szerveren Igen, minden kérésnél Bejelentkezett app, friss adat, publikus tartalom SEO-val
SPA
ssr: false
A böngészőben, JS-futás után Csak a váz kiszolgálásához Admin, belső eszköz — ahol nincs SEO-igény
Prerender
prerender: true
Build-időben, egyszer Nem — statikus fájl Marketing, dokumentáció, blog: a legjobb ár/érték
Hibrid
routeRules
Útvonalanként más Útvonaltól függ A valóság: egy appban mind a három egyszerre
A prerender Workers-en különösen értékes. A build-időben legenerált HTML a .output/public/-ba kerül, tehát static assetként szolgálódik ki. Ez azt jelenti: nem fut Worker, nem fogy CPU-idő, és a Cloudflare hivatalos árazása szerint az assetekre érkező kérés ingyenes. Egy prerenderelt marketing-oldal gyakorlatilag nulla futásidejű költséggel megy — miközben a világ minden pontján az edge-ről érkezik.

7.2routeRules — a teljes eszköztár

A nuxt.config.ts-ben glob-mintákkal rendelsz szabályt útvonalcsoportokhoz:

SzabályMit csinál
prerender: trueBuild-időben legenerálja, statikus fájlként szolgálja ki
ssr: falseAz útvonal kliensoldali marad (SPA-sziget)
swr: 300Nitro-cache 300 mp-re, elavulás után háttérben frissít — lásd a 7.3-at
isr: 300CDN-szintű változat ugyanerre — platformfüggő
cache: {…}A részletes forma: maxAge, staleMaxAge, swr, varies
headers: {…}Fejlécek hozzáadása (cache-control, biztonsági fejlécek)
redirect: {…}Szerveroldali átirányítás, státuszkóddal
cors: trueCORS-fejlécek — a cors npm-csomag helyett
noScripts: trueNem tölti be a Nuxt JS-t: tiszta statikus HTML
appMiddlewareRoute middleware ki-/bekapcsolása útvonalcsoportra
// nuxt.config.ts — egy tipikus SaaS felosztása
export default defineNuxtConfig({
  routeRules: {
    // ① marketing: build-időben statikus — a Worker meg sem hívódik
    '/':            { prerender: true },
    '/arak':        { prerender: true },
    '/blog/**':     { prerender: true },

    // ② publikus, megosztott nézet: ritkán változik, de nem build-time adat
    '/p/**':        { swr: 300 },

    // ③ az alkalmazás: mindig friss, és SENKI ne cache-elje
    '/app/**':      { ssr: true, headers: { 'cache-control': 'private, no-store' } },

    // ④ admin: nincs SEO-igény → nincs SSR-költség sem
    '/admin/**':    { ssr: false },

    // ⑤ API
    '/api/**':      { cors: true, headers: { 'cache-control': 'no-store' } },

    // ⑥ régi URL-ek életben tartása
    '/pricing':     { redirect: { to: '/arak', statusCode: 301 } },
  },
})
A ③-as sor fontosabb, mint amilyennek látszik. A bejelentkezett felület HTML-je felhasználó-specifikus payloadot tartalmaz (5. modul). Ha erre nem teszel private, no-store-t, akkor egy köztes cache — böngésző, vállalati proxy, vagy a te saját szabályod — eltárolhatja, és a következő látogatónak kiszolgálhatja. Ez az a hibaosztály, ami nem a te kódodban van, mégis a te felelősséged.

7.3Az isr és az swr — mit ígér a doksi, és mi történik Workers-en

A Nuxt dokumentációja az ISR/SWR route rule-oknál kifejezetten Vercelt és Netlifyt említi: ott ezek a szabályok platform-natív képességre fordulnak le — a szolgáltató saját CDN-je végzi a tárolást és a háttérben történő frissítést.

Cloudflare-en nincs ilyen natív leképezés. Ott ezek a szabályok a Nitro saját cache-rétegére fordulnak, ami az unstorage storage-rendszerét használja. És itt van a lényeg:

A Nitro cache alapértelmezett tárolója éles környezetben a memory driver. Workers-en ez az izolátum memóriája. Vagyis: a cache nem osztott a datacenterek között, nem osztott az izolátumok között, és eltűnik, amint az izolátumot kiürítik. Ha a szabály be van kapcsolva, technikailag működik — de a találati arány töredéke annak, amit vársz, és nincs semmilyen garancia a tartósságra. Ez nem hiba, hanem konfigurációs alapértelmezés, amit neked kell felülírnod.

A megoldás: tedd a cache-t KV-be

// nuxt.config.ts
export default defineNuxtConfig({
  nitro: {
    preset: 'cloudflare_module',

    storage: {
      // a „cache" mountpontot használja a Nitro cache-rétege (swr/isr/cachedEventHandler)
      cache: {
        driver:  'cloudflare-kv-binding',
        binding: 'CACHE',          // a wrangler.jsonc-ben deklarált binding neve
      },
    },

    // fejlesztés közben maradjon fájlrendszer — gyorsabb és láthatóbb
    devStorage: {
      cache: { driver: 'fs', base: '.nitro/cache' },
    },
  },
})
// wrangler.jsonc
{
  "kv_namespaces": [
    { "binding": "CACHE", "id": "…" }
  ]
}

Ezzel a cache tartós és globálisan elérhető lesz. Cserébe a KV tulajdonságait örökölöd, és ezeket ismerni kell:

KV-tulajdonságMit jelent a cache-edre
Végső konzisztencia (jellemzően ~60 mp)Az írás nem azonnal látszik mindenhol. Cache-nek ez rendben van — „azonnali invalidálás”-nak nem.
Kulcsonként korlátos írási rátaEgy gyakran frissülő kulcsot ne ezen keresztül tarts karban.
Nagyon gyors, globális olvasásPont ezért jó cache-nek: sok olvasás, kevés írás.
És a multitenant csapda ismét. A route-rule alapú cache kulcsa alapvetően az útvonalból képződik. Aldomain- vagy egyedi domain-modellben viszont ugyanaz az útvonal (/p/riport) különböző tenantokhoz tartozik — vagyis ütközhetnek, és az egyik tenant cache-elt oldalát kapja a másik. Mielőtt bármilyen cache-szabályt élesítesz aldomain-modellben, ellenőrizd, hogy a hoszt része-e a kulcsnak (varies: ['host'], illetve saját getKey), és teszteld két tenanttal. Ha bizonytalan vagy: ne cache-elj. Egy lassabb oldal olcsóbb, mint egy adatszivárgás.

A kontrollálhatóbb út: cache a végponton, nem az útvonalon

Sok esetben jobb a routeRules helyett közvetlenül a drága API-végpontot cache-elni — mert ott te írod a kulcsot:

// server/api/public/report/[slug].get.ts
export default defineCachedEventHandler(async (event) => {
  const tenant = event.context.tenant
  const slug   = getRouterParam(event, 'slug')!
  return await buildPublicReport(event, tenant.id, slug)
}, {
  maxAge:      300,     // 5 percig friss
  staleMaxAge: 3600,    // utána még egy óráig kiszolgálható, közben frissül
  swr:         true,

  // ← EZ a lényeg: a kulcsot te írod, és benne van a tenant
  getKey: (event) =>
    `report:${event.context.tenant.id}:${getRouterParam(event, 'slug')}`,
})

Ugyanez függvényszinten is megy (defineCachedFunction), ha nem egy egész végpontot, hanem egy drága részszámítást akarsz cache-elni.

És van egy harmadik réteg is: a Cloudflare saját cache-e, ami a Worker előtt van, és a HTML-t magát tárolja el az edge-en. Ez a leggyorsabb (a Worker el sem indul), de a legkevésbé finomhangolható — és pont ezért itt a legkönnyebb tenantok közti adatot szivárogtatni. Ez a Cloudflare-anyag 18. moduljának témája; ebben a tananyagban a 20. modulban térünk vissza rá, a cache-stratégiánál.

7.4Prerender a gyakorlatban

export default defineNuxtConfig({
  nitro: {
    prerender: {
      crawlLinks: true,                 // követi a talált <NuxtLink>-eket
      routes: ['/', '/arak', '/sitemap.xml'],
      failOnError: true,                // a build bukjon el, ne csendben hiányozzon egy oldal
    },
  },
})

Amit érdemes tudni:

7.5ssr: false szigetek

Az ssr: false egy útvonalcsoportra azt jelenti: a Worker egy üres vázat ad vissza, a tartalom a böngészőben áll össze. Nem visszalépés, hanem tudatos csere.

NyeredVeszíted
Nincs SSR-CPU kérésenként (Workers-en ez pénz)
A váz statikus és cache-elhető
Nincs hydration mismatch, nincs payload-szivárgás
Egyszerűbb kód: csak kliens fut
Nincs SEO és nincs link-előnézet
Lassabb első hasznos festés
A felhasználó spinnert lát (mint a mai SPA-dban)

A tipikus jó jelöltek: /admin/**, belső riportnézetek, összetett szerkesztőfelületek, bármi, ami bejelentkezés mögött van és nagy kliensoldali interaktivitást igényel. Ha az egész appod ilyen, akkor érdemes visszaolvasni az 1. modul „mikor ne Nuxt” szakaszát — nem baj, ha a válasz az, hogy mégis Nuxt, de tudatosan.

7.6Streaming SSR (4.5, kísérleti)

A 4.5 kísérleti SSR-streaminget hozott: a szerver nem várja meg a teljes HTML elkészültét, hanem darabokban küldi. Az elvi haszon a jobb TTFB — a böngésző hamarabb kap fejlécet és fejrészt, hamarabb kezdi tölteni a CSS-t.

Workers-en ez elvileg jó illeszkedés, mert a runtime natívan streamel (a Cloudflare best practices kifejezetten javasolja a stream alapú válaszokat a memóriahasználat miatt). Három dolgot viszont mérlegelj, mielőtt bekapcsolod:

Ajánlás: tartsd szemmel, de most ne erre építs. Egy 2026-os Nuxt-projekten a TTFB-t sokkal többet javítja az, ha a prerender/cache-stratégiád rendben van, mint a streaming.

7.7Döntési térkép a te SaaS-odra

ÚtvonalcsoportAjánlott szabályMiért
Nyitóoldal, árak, funkciókprerender: trueNem változik kérésenként, SEO kell, és így nulla futásidejű költség
Blog, dokumentációprerender: trueUgyanaz; új tartalom = új deploy
Publikus, megosztott nézet (/p/**)swr + KV-cache + tenant a kulcsbanAdatbázisból jön, de ritkán változik és sokan nézik
Bejelentkezett app (/app/**)SSR + private, no-storeFriss adat, felhasználó-specifikus payload — nem cache-elhető
Admin, belső eszközökssr: falseNincs SEO-igény, spórolsz a CPU-időn
API (/api/**)no-store; a drága végpontokon defineCachedEventHandlerA kulcsot te írod, a tenanttal együtt
Auth-oldalak (/login, /register)SSR, no-storeCSRF-token és állapot van bennük
Mi jön ezután? A 8. modullal átlépünk a szerveroldalra: server/api, a h3 event-modellje, validáció, hibakezelés — és egy teljes Express → h3 megfeleltetési tábla, hogy a meglévő API-d mintáit egy az egyben át tudd fordítani.

7.8Ellenőrizd magad

  1. Miért olcsóbb egy prerenderelt oldal Workers-en, mint egy SSR-elt?
    Válasz

    Mert a prerenderelt HTML a .output/public/-ba kerül, és static assetként szolgálódik ki: nem indul el a Worker, nem fogy CPU-idő, és az assetekre érkező kérés nem számít bele a futtatási költségbe. Az SSR-elt oldalnál minden kérés lefuttatja a kódodat.

  2. Bekapcsoltad az swr: 300-at, de a találati arány gyanúsan alacsony. Mi a legvalószínűbb ok Workers-en?
    Válasz

    Hogy a Nitro cache-e az alapértelmezett memory driveren fut, ami Workers-en az izolátum memóriáját jelenti: nem osztott a datacenterek és izolátumok között, és eltűnik, amint az izolátumot kiürítik. A megoldás a cache storage-mount KV-re állítása a nitro.storage-ban, plusz a KV-binding felvétele a wrangler-konfigba.

  3. Miért veszélyes cache-szabályt bekapcsolni aldomain-alapú multitenant appban anélkül, hogy megnéznéd a kulcsot?
    Válasz

    Mert a route-rule cache kulcsa alapvetően az útvonalból képződik, és aldomain-modellben ugyanaz az útvonal különböző tenantokhoz tartozik — vagyis az egyik tenant cache-elt oldalát kaphatja a másik. A hosztot bele kell venni a kulcsba (varies, saját getKey), és két tenanttal tesztelni kell. Bizonytalanság esetén inkább ne cache-elj.

  4. Mikor jobb a defineCachedEventHandler, mint a routeRules-alapú cache?
    Válasz

    Amikor a kulcsot te akarod meghatározni — például mert a tenantnak, a felhasználónak vagy egy szűrőnek benne kell lennie. A getKey-jel pontosan megmondod, mi az azonosító, és a maxAge/staleMaxAge/swr hármassal a viselkedést is te szabod meg. A route rule kényelmesebb, de kevesebb kontrollt ad.

  5. Miért kell private, no-store a bejelentkezett felület HTML-jére?
    Válasz

    Mert a HTML tartalmazza a szerveroldali lekérések payloadját, ami felhasználó-specifikus. Ha egy köztes cache (böngésző, vállalati proxy, saját szabály) eltárolja, a következő látogatónak szolgálhatja ki. Ez nem a kódod hibája, mégis a te felelősséged — és a fejléc az egyetlen, ami megakadályozza.

  6. Melyik útvonalakra érdemes ssr: false, és mit adsz fel érte?
    Válasz

    Bejelentkezés mögötti, SEO-t nem igénylő felületekre: admin, belső riportok, összetett szerkesztők. Nyered: nincs SSR-CPU kérésenként, a váz cache-elhető, nincs hydration mismatch és payload-szivárgás. Feladod: az indexelhetőséget, a link-előnézetet és a gyors első hasznos festést.

Előző6. modul — Állapotkezelés és SSR-csapdák Következő 8. modul — Server routes és h3 — Express-ből érkezve