Udgivelse og stabil drift
Gamle faner efter deployment: undgå blanke sider og manglende frontendfiler
En ny version kan være bygget og deployet korrekt, mens en åben browserfane stadig kører gårsdagens JavaScript. Hvis den gamle kode senere henter en fil, som den nye deployment har fjernet, kan brugeren møde en blank side, en knap der intet gør eller en fejl om et dynamisk modul. Løsningen er en samlet versionsstrategi for HTML, byggede filer, hosting, service worker og backend – ikke endnu et “genindlæs siden”-råd.
Udgivet 2. oktober 2026 · Ca. 12 minutters læsetid
Det korte svar
Lad HTML pege på den aktuelle release, giv uforanderlige filer et nyt navn, når indholdet ændres, og sørg for at gamle faner enten kan hente deres gamle filer eller komme sikkert over på den nye version. Hvis appen bruger en service worker, skal dens opdateringsflow passe til de sider, den styrer. Test altid en release oven på en åben fane fra den forrige produktionsversion.
Hvorfor rammer fejlen kun nogle brugere?
Moderne frontends deler ofte JavaScript i flere filer. Forsiden henter kun det nødvendige, mens rapporter, PDF-eksport eller administrationssider indlæses senere. Buildværktøjet giver typisk filerne et indholdshash, så en ændret fil får en ny URL.
- 1. Brugeren åbner version A. Fanen beholder JavaScript, som kender filnavnene fra A.
- 2. Version B deployes. Nye hashes giver nye filnavne, og hostingplatformen kan fjerne filerne fra A.
- 3. Brugeren åbner en sjældent brugt funktion. Den gamle fane beder nu om en lazy-loaded fil fra A.
- 4. Filen findes ikke længere. Requestet får 404, HTML fra en bred SPA-fallback eller et andet uventet svar, og modulet kan ikke indlæses.
Vites aktuelle dokumentation beskriver netop versionsskævheden mellem en gammel browserklient og slettede dynamiske chunks. Dårligt netværk eller en browserudvidelse kan give en lignende fejl, så kontrollér requestets status, indholdstype og URL, før årsagen konkluderes.
Aftal en releasekontrakt for fire lag
| Lag | Skal være sandt | Kontrol efter deployment |
|---|---|---|
| HTML | En stabil URL kan genvalideres og peger på den aktuelle releases filer | Læs Cache-Control og de faktiske script-URL'er |
| Frontendfiler | Ændret indhold får ny URL, og nødvendige gamle filer lever længe nok | Åbn en gammel fane og indlæs en tidligere ubrugt route |
| Service worker | Én sammenhængende cacheversion styrer siden, eller brugeren vælger et sikkert skift | Opdatér med flere åbne faner og kontrollér waiting/active |
| API og data | Den tidligere frontend kan fortsat tale med den nye backend i overgangsperioden | Kør kritiske requests fra version A mod backend B |
Den konkrete løsning afhænger af hosting og framework. Kontrakten er vigtigere end et bestemt produktnavn: Hvor længe kan gamle klienter leve, hvilke filer skal bevares, og hvordan får brugeren besked, hvis et sikkert versionsskift kræver genindlæsning?
Cache HTML og byggede filer forskelligt
En fil som app.a1b2c3.jskan caches længe, hvis indholdet aldrig ændres under samme URL. En stabil HTML-URL skal derimod kunne genvalideres, fordi den fortæller browseren, hvilke filnavne der udgør den aktuelle release.
# HTML, manifest og andre stabile URL'er, som peger på den aktuelle version
Cache-Control: no-cache
# Byggede filer med indholdshash i filnavnet
Cache-Control: public, max-age=31536000, immutable- Brug indholdshash til uforanderlige filer. Nyt indhold skal have en ny URL; overskriv ikke en langtids-cached fil under samme navn.
- Lad HTML genvalidere. MDN anbefaler
no-cachetil HTML, så et gemt svar kan bruges efter kontrol mod serveren. - Forstå no-store.
no-storeforhindrer lagring; det er noget andet endno-cacheog kan være relevant for følsomme svar. - Kontrollér live headers. Repoets konfiguration er ikke bevis for, hvad browser og CDN faktisk modtager på produktionsdomænet.
Lad ikke SPA-fallback skjule en manglende fil
En client-side route som /sager/42skal ofte omskrives til appens HTML. En request til en manglende JavaScript- eller CSS-fil skal derimod ikke nødvendigvis have samme svar. Hvis hosting returnererindex.htmlmed status 200 til en .js-URL, kan browseren vise en MIME- eller syntaksfejl, som skjuler den egentlige årsag: filen findes ikke.
- Afgræns fallback til navigationsrequests eller udeluk mapper og filtyper med byggede assets efter platformens model.
- Lad en manglende versionsnavngiven fil svare som manglende fil, så overvågning og fejlsøgning kan se problemet.
- Kontrollér status,
Content-Typeog de første bytes i svaret på den konkrete fejlede asset-URL. - Antag ikke, at alle hostingplatforme har samme atomiske deploy- og asset-retention-adfærd.
Microsoft dokumenterer eksempelvis, hvordan en navigation fallback kan ekskludere statiske mapper. Netlify dokumenterer særskilt, at code splitting og hashnavne kan give brudte assetreferencer på tværs af deployments. Brug dokumentationen for den hosting, appen faktisk kører på.
Bevar den gamle vej eller giv en kontrolleret vej over
Den mest rolige overgang er, at en åben version A kan færdiggøre sit arbejde, selv om version B er live. Vites fejlsøgningsguide nævner blandt andet midlertidig bevaring af gamle chunks som en mulig løsning. Om det er muligt, afhænger af hostingplatformens deploymodel og oprydning.
Kompatibilitetsvindue
Bevar tidligere assets og backendkontrakter i en aftalt periode, så gamle faner kan afslutte uden tvungen genindlæsning.
Kontrolleret opdatering
Opdag en kendt versionsfejl, beskyt ikke-gemt arbejde, forklar opdateringen og genindlæs højst én gang til den friske HTML.
En automatisk reload kan være en sidste sikkerhedsventil, men den må ikke skabe en løkke ved netværksfejl, slette en kladde eller skjule alle modulfejl som “ny version”. Log årsag, build-id og resultat, og begræns genindlæsningen med en sessionsmarkør eller en anden tydelig engangskontrol.
Service workeren må ikke blande to releases
Browserens normale service worker-livscyklus lader en ny worker vente, indtil den gamle ikke længere styrer åbne klienter. Det beskytter mod at skifte netværks- og cachelogik midt i en side, der blev indlæst med den forrige version.
- Brug ikke skipWaiting som blind standard. web.dev advarer om, at den nye worker så kan styre sider, som blev indlæst med gammel kode.
- Knyt caches til releases. Slet kun caches, som tilhører appen, og først når den aktive worker ikke længere skal betjene gamle klienter med dem.
- Vis en forståelig opdatering. Lad brugeren gemme eller afslutte kritisk arbejde, før en ventende worker aktiveres og siden genindlæses.
- Test flere faner og installeret PWA. En almindelig refresh er ikke det samme som at lukke alle klienter, som den gamle worker kontrollerer.
Hold backend og data kompatible med gamle klienter
Selv når alle frontendfiler kan hentes, kan en gammel fane fejle mod en ny backend. Bevar felter og adfærd, som den tidligere produktionsversion bruger, gennem det aftalte overgangsvindue. Fjern først gamle API-felter eller databasestrukturer, når måling viser, at relevante gamle klienter ikke længere afhænger af dem.
- Tilføj nye felter og tolerér gammel requestform, før gamle felter gøres obligatoriske eller fjernes.
- Adskil deployment af kompatibel backend fra senere oprydning, hvis ændringen ellers kræver et risikabelt øjebliksskift.
- Versionér en kontrakt, når et reelt brud ikke kan undgås, og mål brugen af den gamle version før udfasning.
- Lad ikke en frontend-reload være rollback for en databasemigrering, der allerede har ændret eller slettet data.
Test opgraderingen som en rigtig bruger
- 1. Åbn den nuværende produktion. Log ind med en kontrolleret testkonto, besøg startsiden og lad en sjældent brugt lazy route være uåbnet.
- 2. Bevar fanen. Gem eventuelt en ufarlig kladde, og lad også en anden fane eller installeret PWA være åben, hvis appen bruger service worker.
- 3. Deploy den nye version. Kontrollér commit, workflow og at den forventede buildversion faktisk er live.
- 4. Brug den gamle fane. Åbn den lazy-loaded funktion, gem en tilladt handling, navigér frem og tilbage, og observer netværk og console.
- 5. Kontrollér opdateringsvejen. Hvis appen beder om reload, skal kladden bevares eller brugeren advares, og den nye side må kun genindlæses kontrolleret.
- 6. Gentag med dårlig forbindelse. Skeln mellem versionsfejl og almindeligt netværksudfald; de må ikke udløse den samme blinde handling.
Automatisér gerne kernen, men behold mindst én browserkontrol, der starter på den gamle produktionsversion. Et testmiljø med kun den nye release kan ikke vise denne fejlklasse.
Gør versionsfejl mulige at diagnosticere
- Vis et kort build- eller commit-id i en supportvisning uden at afsløre secrets.
- Registrér fejlede asset-URL'er, status og aktiv buildversion i dataminimeret fejlovervågning.
- Overvåg 404 og uventet
text/htmlpå versionerede JavaScript- og CSS-stier. - Skeln mellem netværksfejl, blokeret request, manglende fil, forkert MIME-type og API-inkompatibilitet.
- Dokumentér hostingens asset-retention, cache headers, fallbackregler og service worker-opdatering i driftsdokumentationen.
Typiske fejl
- HTML caches som en uforanderlig asset: Browseren bliver ved med at pege på gamle filnavne.
- Hver deployment sletter straks alle gamle filer: Åbne faner mister deres lazy-loaded chunks.
- SPA-fallback rammer alt: En manglende JavaScript-fil bliver besvaret med HTML og en misvisende MIME-fejl.
- Alle importfejl tvinger reload: Netværksfejl eller browserudvidelser skaber datatab eller reload-løkker.
- Ny service worker aktiveres med det samme: Gammel sidekode og ny cachelogik blandes uden et aftalt kompatibilitetslag.
- Kun inkognito testes: Den rene browser springer netop den gamle cache, fane og worker over, som skal afprøves.
- Frontend og backend skal skifte i samme sekund: En langsom klient eller åben fane rammer en kontrakt, der allerede er fjernet.
Tjekliste før næste frontend-release
- ☐ HTML og stabile metadata-URL'er kan genvalideres efter deployment.
- ☐ Ændrede JavaScript- og CSS-filer får nye versionsnavngivne URL'er.
- ☐ Hostingens håndtering og levetid for tidligere assets er kendt.
- ☐ SPA-fallback er afgrænset, så manglende assets kan opdages som manglende.
- ☐ Den tidligere frontend kan bruge den nye backend i overgangsvinduet.
- ☐ Service workerens waiting-, aktiverings- og cacheoprydningsflow er besluttet.
- ☐ En reload ved kendt versionsfejl kan ikke køre i løkke eller slette ikke-gemt arbejde.
- ☐ En gammel fane er testet gennem deployment og en hidtil uåbnet lazy route.
- ☐ Flere faner og installeret PWA er testet, hvis appen bruger service worker.
- ☐ Live headers, statuskoder, MIME-typer, canonical build-id og console er kontrolleret.
- ☐ Overvågning kan skelne manglende assets fra almindelige netværksfejl.
- ☐ Rollback omfatter både frontend, backend, service worker og eventuelle dataændringer.
Relaterede guides
Officielle kilder
Cache-, fallback- og deployadfærd varierer mellem browsere, buildværktøjer og hostingplatforme. Principperne her er verificeret mod aktuelle standard- og leverandørkilder; kontrollér altid den konkrete platforms dokumentation og de live HTTP-svar i appens eget miljø.
- MDN: HTTP-cache og cache-busting med versionsnavngivne filer
- MDN: Cache-Control og forskellen på no-cache og no-store
- Vite: fejl ved dynamiske imports efter en ny deployment
- web.dev: service workerens livscyklus, waiting og skipWaiting
- Netlify: SPA, code splitting og filnavne på tværs af deployments
- Microsoft: afgræns navigationFallback fra statiske frontendfiler