A nuxt build két különböző dolgot állít elő, és a Cloudflare két különböző módon szolgálja ki őket — az egyiket ingyen és korlátlanul, a másikat számlázva. Ez a modul ezt a kettősséget bontja szét, végigmegy a wrangler.jsonc minden során, és megmutatja a verzió-alapú kigördítést, amivel a globálisan azonnali deploy mégis biztonságossá tehető.
A 2. modulból tudod, hogy a build végén egy .output/ könyvtár keletkezik. Deploy szempontjából ez két, gyökeresen eltérő természetű dolog egy mappában:
prerender).A Cloudflare alapértelmezett sorrendje egyszerű: ha a kért URL egyezik egy fájllal az asset-könyvtárban, azt szolgálja ki — anélkül, hogy a Worker-kód lefutna. Csak ha nincs találat, kerül a kérés a Workerhez.
GET /_nuxt/entry.a1b2c3.js → asset találat → kiszolgálva, a Worker nem fut
GET /termek/bogre → nincs asset → Worker: SSR
GET /api/orders → nincs asset → Worker: server route
| Beállítás | Érték | Jelentés |
|---|---|---|
directory | ".output/public" | a statikus fájlok helye — Nuxt-nál mindig ez |
binding | "ASSETS" | opcionális: env.ASSETS.fetch() a Worker-kódból |
run_worker_first | false (alap) | asset előbb; true: mindig a Worker fut |
run_worker_first | ["/api/*", "!/api/docs/*"] | mintánként; a !-jel kezdődő minta erősebb |
not_found_handling | "none" (alap) | SSR-appnál ez kell — a nem talált út a Workerhez esik |
not_found_handling | "single-page-application" | minden az index.html-re esik vissza |
not_found_handling | "404-page" | a legközelebbi 404.html-t szolgálja ki |
single-page-application-re egy SSR-es Nuxt-appnál. Ez a beállítás minden nem-talált útvonalat az index.html-lel válaszol meg — vagyis a Workered soha nem fut le a dinamikus oldalakra, és az egész alkalmazásod csendben SPA-vá degradálódik. Ez csak akkor helyes, ha tudatosan ssr: false-szal dolgozol (mint a scoope). SSR-nél hagyd a none alapértelmezést.Itt kapcsolódik össze a 7. modul a mostanival, és ez a kapcsolat okoz éles hibákat. Ha egy útvonalra prerender: true route rule-t tettél, abból fájl lesz a .output/public/-ban. Fájl → asset → a Worker nem fut le rá.
Ami azt jelenti, hogy azon az útvonalon:
Ez nem hiba, hanem a prerendering lényege — de meg kell lepődni tudni rajta. A klasszikus baleset: kiteszel egy prerender: true-t a marketing-főoldalra, majd három hónappal később valaki felvesz egy server middleware-t, ami minden kérésnél naplózza a látogatót — és nem érti, miért hiányzik a főoldal a statisztikából.
run_worker_first mintás formája: "run_worker_first": ["/kampany/*"]. Fizetni fogsz érte, de lefut a szerveroldali logika. Ingyenes tréfaként ne használd: a Free csomagon, ha a run_worker_first által elkapott kérésekkel eléred a kérés-limitet, azok 429-et kapnak, nem esnek vissza a statikus kiszolgálásra.wrangler.jsonc anatómiája — soronkéntÍme egy éles Nuxt-app teljes konfigurációja, kommentekkel. A $schema-sor nem kötelező, de a szerkesztőd ettől fogja tudni, mit ajánljon fel.
{
"$schema": "node_modules/wrangler/config-schema.json",
// ——— identitás ———
"name": "bolt",
"main": ".output/server/index.mjs", // a Nitro kimenete — build UTÁN létezik
"compatibility_date": "2026-08-01",
"compatibility_flags": ["nodejs_compat"], // Nuxt-nál gyakorlatilag kötelező
// ——— a statikus fele ———
"assets": {
"directory": ".output/public",
"binding": "ASSETS" // csak ha kódból is olvasnál assetet
},
// ——— megfigyelhetőség (16. modul) ———
"observability": { "enabled": true, "head_sampling_rate": 1 },
"upload_source_maps": true,
// ——— futásidő ———
"placement": { "mode": "smart" }, // megfontolandó — lásd 17.9
"limits": { "cpu_ms": 50 }, // önvédelem a végtelen ciklus ellen
// ——— konfiguráció (12. modul) ———
"vars": {
"NUXT_PUBLIC_SITE_URL": "https://bolt.hu"
},
// ——— bindingok (11. modul) ———
"hyperdrive": [{ "binding": "DB", "id": "…" }],
"kv_namespaces": [{ "binding": "CACHE", "id": "…" }],
"r2_buckets": [{ "binding": "MEDIA", "bucket_name": "bolt-media" }],
// ——— hova válaszoljon ———
"routes": [{ "pattern": "bolt.hu", "custom_domain": true }],
"workers_dev": false, // éles appnál kapcsold ki
"preview_urls": true, // de a preview URL-eket hagyd meg!
// ——— környezetek ———
"env": {
"staging": {
"name": "bolt-staging",
"routes": [{ "pattern": "staging.bolt.hu", "custom_domain": true }],
"vars": { "NUXT_PUBLIC_SITE_URL": "https://staging.bolt.hu" },
"hyperdrive": [{ "binding": "DB", "id": "…másik…" }],
"kv_namespaces": [{ "binding": "CACHE", "id": "…másik…" }],
"r2_buckets": [{ "binding": "MEDIA", "bucket_name": "bolt-media-staging" }]
}
}
}
vars nem. Ha felveszel egy új KV-namespace-t felül, és nem másolod be az env.staging-be is, a staging-deploy simán lefut — és a stagingben egyszerűen nem létezik a binding. Futásidőben derül ki, undefined formájában. A 12. modul konfigurációs ellenőrzőlistája pont ezért kezdődik azzal, hogy környezetenként listázd ki a bindingokat.A workers_dev: false és a preview_urls: true párost érdemes tudatosan beállítani. Az első kikapcsolja az appnév.subdomain.workers.dev címet — éles alkalmazásnál ez kívánatos, mert különben az appod két különböző URL-en érhető el (SEO-duplikáció, és egy nyitva felejtett hátsó ajtó). A második viszont maradjon bekapcsolva, mert a 17.6-os workflow épp erre épül. A preview_urls alapértéke a workers_dev értéke, tehát ha az elsőt kikapcsolod, a másodikat explicit vissza kell kapcsolnod.
Ugyanaz a wrangler.jsonc négyféle úton juthat el a Cloudflare-hez. Nem ízlés kérdése, melyiket választod — a csapat mérete és a megfelelőségi igény dönti el.
| Modell | Hogyan | Mikor jó | Mikor rossz |
|---|---|---|---|
| Laptopról | npx wrangler deploy | egyszemélyes projekt, prototípus, sürgős javítás | csapatban: nincs nyoma, ki mit tolt ki |
| GitHub Actions | cloudflare/wrangler-action vagy nyers CLI | ha már van CI-d; teljes kontroll a lépések felett | API-tokent kell tárolni és forgatni |
| Workers Builds | repo bekötése a dashboardon | ha nem akarsz CI-t üzemeltetni; pushra épít és deployol | kevesebb kontroll; a Worker nevének egyeznie kell a configgal |
| IaC (Alchemy) | alchemy.run.ts | több worker, több stage, infrastruktúra kódban | plusz absztrakciós réteg, plusz tanulási görbe |
# a minimum, ami mindegyik modellben ugyanaz
npx nuxt build # előállítja a .output/-ot
npx wrangler deploy # feltölti a Workert + az asseteket
main és az assets.directory is a .output/-ra mutat, ami a build előtt nem létezik. Ha a CI-ben a deploy-lépés valaha „file not found”-dal hasal el, majdnem biztos, hogy elmaradt vagy elhasalt a build. Tedd egy package.json scriptbe, hogy ne lehessen elrontani: "deploy": "nuxt build && wrangler deploy".A Workers-deploy globális és azonnali — nincs „egy régióban élesítjük, aztán figyeljük”. Ez remek, amíg jó a kód, és katasztrófa, amikor nem. A Cloudflare erre a verzió fogalmát adja: a feltöltést és a forgalomra állítást szétválasztja.
# ① feltöltés — verzió jön létre, de NEM kap forgalmat
npx wrangler versions upload
# ② forgalomra állítás, akár fokozatosan
npx wrangler versions deploy # interaktív: százalékok bekérése
npx wrangler versions deploy <uj-id>@10 <regi-id>@90
# ③ állapot és visszalépés
npx wrangler versions list
npx wrangler deployments status
npx wrangler rollback <verzio-id> --message "500-asok a checkoutban"
| Fogalom | Mit jelent |
|---|---|
| verzió | egy feltöltött kódállapot + konfiguráció; önmagában nem kap forgalmat |
| deployment | egy vagy több verzió, százalékos forgalommegosztással |
| fokozatos kigördítés | két verzió párhuzamosan él, megadott arányban |
| version affinity | egy felhasználót ugyanahhoz a verzióhoz köt a kigördítés idejére |
| rollback | azonnali visszaállás egy korábbi verzióra |
Két dokumentált korlátot érdemes fejben tartani: fokozatos kigördítésben csak az utolsó 100 feltöltött verzió vehet részt, és egy Durable Objectből egyszerre csak egy verzió futhat — vagyis ha van DO-d, a kigördítés nem tudja két verzióra osztani.
A dokumentáció világosan fogalmaz: minden kérés függetlenül van irányítva a beállított százalékok szerint. Egymás utáni kérések tehát különböző verziókhoz kerülhetnek. Egy Nuxt-appnál ennek konkrét következménye van.
A felhasználó megkapja a B verzió által renderelt HTML-t, benne a B verzió chunk-hivatkozásaival (/_nuxt/entry.uj-hash.js). A böngésző azonnal kéri a chunkot — és ez a kérés a százalékos szórás miatt az A verzióhoz kerülhet, ahol az a fájl nem létezik. Eredmény: 404 a chunkra, vagyis pontosan az a chunk-hiba, amit a 16.9-ben tárgyaltunk — csak most nem deploy után, hanem deploy közben, tartósan.
version affinity, amit a Cloudflare pont erre a problémaosztályra ad: a felhasználót a kigördítés idejére egy verzióhoz köti, így a HTML és a hozzá tartozó chunkok ugyanabból a verzióból jönnek. Ha fokozatos kigördítést használsz egy SSR-es Nuxt-appon, ez nem opcionális finomhangolás, hanem előfeltétel. Alternatíva: ne gördíts fokozatosan, hanem használd a verziókat csak füsttesztre (17.6), és utána állítsd 100%-ra egy lépésben.Minden feltöltött verzió kap egy saját, azonnal elérhető URL-t, forgalom nélkül. Ez az a pont, ahol a 16.6-ban felépített füstteszt bekapcsolódik.
# verziós preview URL — automatikus, hash-elt előtaggal
npx wrangler versions upload
# → https://a1b2c3d4-bolt.<subdomain>.workers.dev
# aliasolt preview URL — beszédes és állandó
npx wrangler versions upload --preview-alias pr-142
# → https://pr-142-bolt.<subdomain>.workers.dev
Az alias formátuma <alias>-<worker-név>.<subdomain>.workers.dev; kisbetű, szám és kötőjel megengedett, a worker névvel együtt legfeljebb 63 karakter. Ezzel a PR-számból generált alias tökéletes: minden pull requestnek stabil, megosztható URL-je lesz, amit a review-ban a terméktulajdonos is meg tud nyitni.
wrangler tail, sem a Logpush. Vagyis a preview URL kiválóan alkalmas arra, hogy eldöntsd, működik-e, de arra nem, hogy kiderítsd, miért nem. Ha a füstteszt elbukik rajta, két utad van: reprodukáld lokálisan wrangler dev-vel (16.6), vagy állítsd a verziót kis százalékra és tailelj --version-id-vel. Ezen kívül: Durable Objectet használó Workernél nem generálódik preview URL, és a preview csak workers.dev aldomainen futhat.Itt egy olyan zavar keletkezik, ami sok időt tud elvinni: a Nuxt-nak és a wranglernek is van „környezet” fogalma, és nem ugyanaz a kettő.
| Réteg | Mechanizmus | Mikor hat |
|---|---|---|
| Nuxt | $production, $development, $env: { staging: {} } + --envName | build időben — a lefordított kimenetbe ég bele |
| Wrangler | env.staging + --env staging | deploy időben — melyik Workerhez és bindingokhoz |
| Runtime | vars, secrets, NUXT_* env-felülírás | futás közben — a runtimeConfig-ot írja felül |
# a helyes párosítás: MINDKETTŐT meg kell adni
npx nuxt build --envName staging # Nuxt-oldali build-konfig
npx wrangler deploy --env staging # melyik Worker + melyik bindingok
$env. Ami cím vagy erőforrás (melyik adatbázis, melyik bucket, melyik domain), az wrangler-oldali env. Ha egy értéket futásidőben is akarsz változtatni deploy nélkül, akkor egyik sem: az vars vagy secret, és a runtimeConfig-on át olvasod (12. modul).# interaktívan bekérve
npx wrangler secret put NUXT_SESSION_PASSWORD
npx wrangler secret put NUXT_SESSION_PASSWORD --env staging
# CI-ből, csővezetéken
echo "$SESSION_PASSWORD" | npx wrangler secret put NUXT_SESSION_PASSWORD
# egy KONKRÉT verzióhoz (a fokozatos kigördítés miatt fontos)
npx wrangler versions secret put STRIPE_KEY
--keep-vars csapdája. Alapból a wrangler deploy a konfigurációdat tekinti igazságnak, és törli a dashboardon kézzel felvett változókat. Ha valaki a csapatból „csak gyorsan” beállított valamit a felületen, azt a következő CI-deploy eltünteti — órákig lehet keresni, miért „romlott el magától” az app. Két helyes megoldás van: vagy minden a repóban van (ajánlott), vagy tudatosan keep_vars: true-t állítasz. A „néha így, néha úgy” a rossz megoldás.A Smart Placement azt csinálja, hogy a Workeredet nem a felhasználóhoz, hanem a backendedhez közel futtatja. Kapcsoló egy sor:
"placement": { "mode": "smart" }
Elsőre ez visszafelé hangzik — nem az volt az edge lényege, hogy közel legyünk a felhasználóhoz? De gondold végig egy SSR-oldal aritmetikáját. Tegyük fel, hogy a renderelés három egymás utáni lekérdezést igényel egy frankfurti Postgresből, és a felhasználó Budapesten van:
| Worker Budapesten | Worker Frankfurtban (smart) | |
|---|---|---|
| Felhasználó → Worker | ~5 ms | ~15 ms |
| 3 × körút az adatbázishoz | 3 × 20 ms = 60 ms | 3 × 1 ms = 3 ms |
| Összesen | ~65 ms | ~18 ms |
A logika: egy lassú körutat cserélsz N lassú körútra. Minél többször fordul a kódod az adatbázishoz egyetlen kérés alatt, annál nagyobb a nyereség.
És — ez a lényeges rész — a statikus asseteket ez nem érinti: azokat a Cloudflare mindig a kéréshez legközelebbi helyről szolgálja ki, függetlenül a Worker elhelyezésétől. Vagyis nem áldozod fel a képek és JS-fájlok sebességét.
| Kapcsold be, ha… | Hagyd ki, ha… |
|---|---|
| Hyperdrive + egyrégiós Postgres van mögötted (4. modul) | az oldalaid többsége előrenderelt vagy cache-elt |
| egy SSR-oldal 2-nél több egymás utáni lekérdezést csinál | egy kérés alatt legfeljebb egyszer fordulsz adatért |
| külső API-t hívsz, ami egy régióban van | KV-ből és R2-ből olvasol (azok maguk is elosztottak) |
fetch eseménykezelőre hat — RPC-metódusokra és nevesített belépőpontokra nem. (2) Csak olyan helyeket vesz figyelembe, ahol a Worker korábban már futott, és egyenletes forgalom kell több helyről ahhoz, hogy döntést tudjon hozni — vagyis egy friss, alacsony forgalmú appon nem fog látványosan működni. (3) A kérések 1%-át szándékosan nem irányítja a Smart Placement, hogy legyen összehasonlítási alapja. Ha méred a hatást, ezt az 1%-ot ne keverd bele a következtetésbe.A 11. modul séma-migrációi és ez a modul itt találkoznak, és ez a találkozás okozza a legcsúnyább éles hibákat. A kérdés egyszerű: előbb a migráció vagy előbb a kód?
A válasz az, hogy egyik sorrend sem biztonságos önmagában, mert bármelyiket választod, lesz egy időablak, amikor a futó kód és a séma nem illik össze. Workers-en ez rosszabb, mint máshol: a deploy globálisan azonnali, fokozatos kigördítésnél pedig szándékosan két kódverzió fut egyszerre.
Az egyetlen működő minta a bővítés-szűkítés (expand–contract), három deployban:
| Lépés | Migráció | Kód |
|---|---|---|
| ① Bővítés | új oszlop hozzáadva, nullable, régi megmarad | — |
| ② Átmenet | — | mindkét oszlopot írja, az újat olvassa (fallback a régire) |
| ③ Visszatöltés | a régi adat átmásolva az újba | — |
| ④ Szűkítés | a régi oszlop eldobva | már csak az újat írja és olvassa |
wrangler rollback nem fog megmenteni: a kódot visszaállítja, az adatbázist nem. Ez a modul egyetlen olyan tanácsa, amit érdemes a csapat írott szabályai közé tenni.| Tünet | Ok | Megoldás |
|---|---|---|
| „Script startup exceeded CPU time limit” | modul-scope inicializálás > 1 s | lusta inicializálás kérésenként (15. modul); wrangler check startup |
| „Script too large” | bundle a 3 / 10 MB gzip limit fölött | wrangler deploy --dry-run --outdir, majd a nagy import kigyomlálása |
Cannot find module 'node:…' | hiányzó nodejs_compat flag | compatibility_flags: ["nodejs_compat"] |
| Deploy „file not found” | nem futott le a nuxt build | egyetlen npm-scriptbe kötni a kettőt |
Binding undefined stagingben | bindingok nem öröklődnek környezetbe | minden binding megismételve env.<név> alatt |
| Minden oldal SPA-ként viselkedik | not_found_handling: "single-page-application" | vissza "none"-ra |
| Egy oldalon nem fut a middleware | az oldal előrenderelt → assetként szolgálódik ki | prerender levétele vagy run_worker_first mintával |
| Chunk-404 kigördítés közben | HTML és chunk különböző verzióból | version affinity, vagy egylépéses 100% |
| „Magától eltűnt egy változó” | deploy felülírta a dashboard-változókat | minden a repóba, vagy tudatos keep_vars |
| Stack trace értelmezhetetlen | nincs source map | nitro.sourceMap + upload_source_maps (16.8) |
Mielőtt egy Nuxt-app először élesbe megy Workers-en, ezeken menj végig. Mindegyik egy-egy korábbi modul zárókövét húzza be:
nodejs_compat bekapcsolva, compatibility_date friss és tudatosan választott.assets.directory a .output/public, a not_found_handling az alapértelmezett none.NUXT_SESSION_PASSWORD legalább 32 karakter (10. modul).observability.enabled és upload_source_maps bekapcsolva — a hibakeresés előfeltételei (16. modul).workers_dev: false, hogy az app ne legyen elérhető két URL-en.300 ezret. A statikus assetekre érkező kérések ingyenesek és korlátlanok, és nem számítanak Worker-kérésnek — csak azok számláznak, amelyek ténylegesen meghívják a Worker-kódot. Tárolási díj sincs az assetekre.
prerender: true route rule-lal ellátott oldalon?
Mert az előrenderelt oldalból HTML-fájl lesz a .output/public/-ban, a Cloudflare pedig alapból az asseteket ellenőrzi először: ha találat van, kiszolgálja a Worker meghívása nélkül. Ha mégis kell a szerverkód, run_worker_first mintával kérheted.
not_found_handling: "single-page-application"-t egy SSR-es Nuxt-appon. Mi történik?
Minden nem-talált útvonal az index.html-t kapja vissza, tehát a Worker soha nem fut le a dinamikus oldalakra — az app csendben SPA-vá degradálódik, SSR nélkül. SSR-nél az alapértelmezett none kell.
--env staging-gel, és stagingben undefined. Miért?
Mert a bindingok és a vars nem öröklődnek a környezetekbe — a kulcsok többsége igen, ezek nem. Minden bindingot meg kell ismételni az env.<név> blokkban.
wrangler deploy és a wrangler versions upload között?
A deploy feltölt és azonnal 100%-os forgalomra állít. A versions upload csak feltölt: létrejön egy verzió, kap egy preview URL-t, de nem kap éles forgalmat, amíg egy versions deploy oda nem irányítja.
Minden kérés függetlenül irányítódik a százalékok szerint, így a felhasználó a B verzió HTML-jét kaphatja, a chunk-kérése viszont az A verzióhoz mehet, ahol az a hash nem létezik. Megoldás: version affinity (a felhasználót egy verzióhoz köti), vagy fokozatos kigördítés helyett egylépéses 100%.
Preview URL-eken nem érhetők el a logok — sem a Workers Logs, sem a wrangler tail, sem a Logpush. Vagy lokálisan reprodukálod wrangler dev-vel, vagy kis százalékra állítod a verziót és wrangler tail --version-id-vel figyeled.
A Nuxt $env / --envName build időben hat (a kimenetbe ég), a wrangler env.<név> / --env deploy időben (melyik Worker, melyik bindingok). Mindkettőt meg kell adni: nuxt build --envName staging && wrangler deploy --env staging.
Akkor éri meg, ha egy SSR-kérés alatt többször, egymás után fordulsz egy egyrégiós adatforráshoz (tipikusan Hyperdrive + Postgres): egy lassú körutat cserélsz N lassúra. A statikus asseteket nem érinti — azokat mindig a kéréshez legközelebbi helyről szolgálja ki. Csak a fetch handlerre hat, és egyenletes forgalom kell a döntéshez.
wrangler rollback egy elrontott séma-migráció után?
Mert a rollback a kódot állítja vissza, az adatbázist nem. Ha a migráció egy lépésben eldobott vagy átalakított valamit, a régi kód már nem tud dolgozni az új sémával. Ezért kell bővítés-szűkítés (expand–contract): minden köztes állapotban két szomszédos kódverzió együtt tud élni.