...

Forståelse af HTTP-betinget caching med ETag og Last-Modified

HTTP-caching sparer tid og data, da jeg kun indlæser ressourcerne igen, hvis de rent faktisk er blevet ændret. Om ETag og Sidst ændret kontrollerer jeg ved hjælp af en betinget forespørgsel, om serveren svarer med 304 Not Modified, hvilket reducerer dataoverførslen og serverbelastningen betydeligt.

Centrale punkter

Følgende hovedpunkter viser, hvad jeg lægger vægt på ved betinget caching med ETag og Sidst ændret opmærksomhed.

  • Mindre trafik: Uændrede filer returnerer en 304-status, ikke hele brødteksten – det reducerer datamængden og ventetiden mærkbart.
  • Bedre ydeevne: Kortere ventetider forbedrer brugeroplevelsen og Core Web Vitals, hvilket SEO hjælper.
  • To mekanismer: Last-Modified/If-Modified-Since og ETag/If-None-Match sikrer en pålidelig validering af cachen.
  • Cache-kontrol: Direktiver styrer opdatering, genindlæsning og adfærd i mellemliggende cacher.
  • Kombination: De to metoder tilsammen sikrer høj nøjagtighed og enkle alternativløsninger.

Først tjekker jeg, hvilke ressourcer der skifter ret ofte, og hvilke der sjældent gør det. For filer, der sjældent ændres, sætter jeg en Sidst ændret-tidspunktet og tilføjer en ETag. Til dynamiske svar foretrækker jeg at bruge ETag, fordi enhver ændring i indholdet straks kan ses. På den måde aflaster jeg serverne, reducerer ventetiderne og leverer meget hurtige sider til tilbagevendende besøgende. Denne strategi styrker Core Web Vitals og dermed indirekte synligheden.

HTTP-betinget caching: Sådan kontrollerer jeg gyldigheden

Når der foretages et nyt opkald, sender klienten ud over GET-metoden yderligere headere, som jeg analyserer på serversiden. Hvis ressourcen har det samme ETag Hvis den er den samme som i cachen (If-None-Match), returnerer jeg 304 Not Modified uden indhold. Hvis der ikke er sket ændringer i tidsstemplet (If-Modified-Since), reagerer serveren ligeledes med 304. Hvis dato eller klokkeslæt ikke længere stemmer, sender jeg 200 OK med nyt indhold samt opdateret Sidst ændret og ETag. På den måde sparer jeg båndbredde, holder cachen opdateret og sikrer mærkbart hurtigere indlæsningstider.

Last-Modified og If-Modified-Since i dagligdagen

Overskriften Sidst ændret Jeg baserer mig på filens faktiske ændringstidspunkt, f.eks. fra filsystemet. Hvis der senere kommer en anmodning med »If-Modified-Since«, og ressourcen ikke er ændret siden da, svarer jeg med 304. Denne metode er ukompliceret, let at forstå og ideel til statiske ressourcer som CSS, JS eller billeder. Begrænsningerne ligger i sekundrasteret for HTTP-tidsstempler og situationer, hvor indholdet ændrer sig logisk, uden at der findes et entydigt filtidspunkt. Hvor Last-Modified støder på sine begrænsninger, supplerer en ETag kontrollen.

ETag og If-None-Match i dynamiske systemer

En ETag genererer jeg som en hash, et versions-ID eller fra en database-kolonne, der markerer statusændringer. Ved gentagen adgang sender browseren If-None-Match, jeg sammenligner tagget med min aktuelle værdi og svarer derefter med 304 eller 200. Denne sammenligning registrerer enhver meningsfuld indholdsændring uden at stole på filens tidsstempel. Især ved API'er, sammensatte sider eller personaliserede fragmenter giver det meget nøjagtige resultater. Det er vigtigt, at jeg holder ETags konsistente i klyngemiljøer, så ingen server tilfældigt bruger en anden Dag produceret.

Sådan kombinerer du Cache-Control korrekt

Med Cache-kontrol Her definerer jeg, hvor længe indholdet betragtes som opdateret uden yderligere forespørgsel, og hvornår browseren skal revalidere det. Jeg indstiller passende max-age-værdier afhængigt af, hvor ofte der sker ændringer, og bruger must-revalidate, hvis forældede data ville være kritiske. For versionerede filer er en lang gyldighedsperiode velegnet, mens svar, der ændres ofte, har en kortere levetid og derefter kan kontrolleres nøjagtigt via ETag eller dato. På denne måde kombinerer jeg korte svartider med korrekt aktualitet. Hvis du vil læse mere om emnet, finder du mange eksempler under Cache-Control-strategier, som jeg bruger i praksis.

Sådan foregår et betinget GET-anmodning trin for trin

Ved den første anmodning returnerer serveren 200 OK med Cache-Control, Sidst ændret og ETag, gemmer browseren det hele. Ved næste besøg afgør alderen i cachen, om der er behov for en revalidering. Er det nødvendigt, sender browseren en forespørgsel med If-None-Match og/eller If-Modified-Since. Hvis værdierne stemmer overens med den aktuelle tilstand, sender jeg 304 Not Modified, og klienten bruger sin cache videre. Hvis de ikke længere passer, følger 200 OK med en ny body og opdateret Valideringsdata.

Sammenligning: ETag vs. Last-Modified

Begge metoder giver mig kontrol, men adskiller sig med hensyn til arbejdsbyrde, nøjagtighed og egnethed. Sidst ændret udmærker sig ved enkel implementering og klar semantik, så længe jeg har korrekte tidsstempler. ETag gengiver indholdet meget præcist, men kræver lidt logik at generere. I mange opsætninger kombinerer jeg begge dele og drager dermed fordel af enkelhed plus præcis genkendelse. Den følgende tabel opsummerer typiske egenskaber og hjælper med beslutningen.

Aspekt Sidst ændret ETag Hint
Identitet Tidspunkt for seneste ændring Indholds-hash eller versions-ID Tid vs. indholdsbaseret identifikator
Ændringsregistrering Sekundopløsning, indirekte Direkte rettet mod indholdet ETag registrerer selv de mindste Forskelle
implementering Meget let, filsystemet er tilstrækkeligt Kræver generering og konsistens Klynger har brug for ens ETags
Brug Statiske ressourcer Dynamiske svar Kombinationen dækker mange Sager fra
Svar på spørgsmål 304 med uændret tidsstempel 304 ved identisk tag 200 ved ændringer med nyt Værdi

Praksis: Effektiv levering af statiske ressourcer

Statiske filer som CSS, JS og billeder ændres sjældent og egner sig til lange max-alder-tider. For filer med versionsstyring indstiller jeg lange tidsintervaller på op til et år og markerer dem som uforanderlige, så browseren indlæser dem uden at spørge om tilladelse. For ikke-versionerede assets vælger jeg kortere tidsfrister og stoler på revalidering via ETag og Last-Modified. På den måde undgår jeg forældet indhold og holder trafikken nede. Hvis jeg sørger for ikke at Sabotage af cache-header ved at lade det stå, opnår jeg en høj hitrate i cachen.

Praksis: API'er og dynamiske sider

Når det gælder API'er, foretrækker jeg som regel ETags, som jeg danner ud fra det serialiserede resultat eller en versionskolonne. Hvis dataposten ændres, genererer jeg en ny tag, hvilket klienterne straks registrerer. For indhold med usikkert tidsstempel undgår jeg ofte Last-Modified, så der ikke opstår et forkert indtryk af aktualitet. Derudover styrer jeg levetiden via Cache-Control og tvinger til revalidering efter udløb. På den måde holder jeg dataene pålideligt opdaterede uden at gøre svarene unødvendigt store.

Test og overvågning af cache-hitrate

Jeg tjekker overskrifter som ETag, Last-Modified, If-None-Match og If-Modified-Since i udviklerværktøjerne. Her holder jeg øje med responskoderne, især 304 kontra 200, for at se, hvor effektiv min revalidering er. Hvis 304 sjældent forekommer, justerer jeg Cache-Control, gyldighedsperioder og ETag-generering. Logfiler og målinger viser mig, hvilke stier der svarer unødvendigt meget. Til samlede forbedringer bruger jeg gerne en Pakke med betingede anmodninger, der samler konfiguration og test.

Hostingarkitektur og ETag-fælder

I opsætninger med flere servere skal en ETag skal være uafhængig af instansen, ellers mislykkes genkendelsen. Jeg sørger for, at alle noder bruger den samme logik og den samme nøgle til genereringen. Reverse proxyer eller CDN'er må ikke ændre ETag'er og bør videresende statusheadere korrekt. Ved implementeringer med asset-fingerprints undgår jeg server-side genberegning af ETag, hvis filen allerede har en versioneret URL. Ensartede regler forhindrer inkonsekvente svar og holder cache-hitrate høj.

Friskhed kontra validering: Præcis anvendelse af direktiver

Jeg skelner klart mellem Friskhed (hvor længe må en cache bruge en kopi uden at spørge om lov?) og Validering (hvordan tjekker jeg, om den stadig er gyldig?). Om Cache-kontrol styrer jeg begge dele meget detaljeret: max-alder fastlægger levetiden hos klienten, s-maxage til delte cacher såsom proxyservere. offentlig tillader cachelagring i delte cacher, privat begrænser det til den endelige browser. skal-revalidere tvinger til forespørgsler efter udløb, mens uforanderlig undgår unødvendige revalideringer af versionerede aktiver. no-cache forbyder ikke caching, men kræver altid en revalidering; ingen opbevaring forbyder derimod fuldstændigt lagring. Ældre Udløber-Header bruger jeg kun som en sikkerhedsforanstaltning; jeg flytter konsekvent logikken over til Cache-Control. Og hvis jeg vil afbøde udfald, hjælper stale-while-revalidate og stale-if-fejl, for at kunne dele indhold, der er udløbet på kort sigt, mens jeg opdaterer i baggrunden eller omgår fejl.

Stærke og svage ETags, komprimering og varianter

Jeg skelner bevidst mellem stærke og svage validatorer. Stærke ETags identificerer nøjagtig den samme repræsentation – ideelt, hvis jeg også Anmodninger om rækkevidde ønsker at betjene effektivt. Svage ETags (Præfiks W/) er tilstrækkeligt, hvis semantisk ensartethed er nok, f.eks. ved små, irrelevante formatændringer. Det vigtige er håndteringen af Kompression: Hvis jeg leverer indhold, der er kodet med både gzip og brotli, må en enkelt ETag ikke gælde for alle varianter. Enten skal jeg generere ETag'en ud fra den ukomprimerede version og derudover angive en passende Vary: Accept-kodning, eller jeg genererer ensartede, men forskellige ETags for hver variant. På den måde undgår jeg fejlresultater og 200-svar, der egentlig burde være 304. Ved If-område Jeg kombinerer rækkeviddeforespørgsler med en validator: Hvis ETag eller datoen stemmer overens, svarer jeg med 206 Partial Content; ellers returnerer jeg 200 med fuld body, så klienten har et konsistent grundlag.

At mestre Vary-headere og indholdsforhandling til fulde

Når serveren leverer forskellige repræsentationer afhængigt af kravene, sætter jeg Varierer korrekt. Typiske eksempler er Accept-Encoding (kompression), Accept-sprog (lokalisering) eller specifikke feature-flags. Jeg undgår at bruge flygtige headere som Brugeragent eller endda Kage at variere, fordi det ødelægger cache-hitraten fuldstændigt. Når personalisering er nødvendig, markerer jeg svar som privat eller ingen opbevaring og adskiller dem tydeligt fra ressourcer, der kan caches offentligt. Vigtigt: Variationer påvirker også ETags – hver variant skal have sin egen, konsistente validator. På den måde sikrer jeg, at browsere, proxyservere og CDN'er anvender den samme logik, og at ingen variant ved en fejltagelse blandes sammen med en anden.

Betingede forespørgsler ud over GET

Betingede anmodninger virker ikke kun ved læsning. Til skrivende metoder bruger jeg Hvis-match eller If-Unmodified-Sincetil manglende opdateringer at forhindre. Hvis klienten ved en PUT- eller DELETE-anmodning returnerer den senest set ETag via Hvis-match hvis serverens tilstand stadig er den samme, gennemfører jeg ændringen – ellers svarer jeg med 412 Forudsætning opfyldt. For at holde styr på klienterne kan serveren desuden 428 Forudsætning påkrævet etablere. Til hurtige test uden body bruger jeg HEAD, der giver mig de samme overskrifter som en GET-anmodning; perfekt, når jeg vil teste metadata. Og ved 304-I svarene medtager jeg igen alle de headere, der er relevante for cachen (Cache-Control, ETag, Expires, Last-Modified), så klienten kan opdatere sine metadata uden at overføre selve indholdet.

Sikkerhed, databeskyttelse og compliance

Jeg gemmer ikke personlige eller følsomme oplysninger i den offentlige cache. Her bruger jeg Cache-kontrol: privat eller ingen opbevaring, så browseren eller ingen instans overhovedet gemmer indholdet. Vær forsigtig med brugerkonti og dashboards: Svar med Sæt cookie eller Autorisation må ikke ved en fejltagelse kunne caches offentligt. ETags kan i sig selv misbruges som sporingsvektorer, hvis de forbliver uændrede i længere tid. Jeg imødegår dette ved kun aktivt at anvende validatorer der, hvor caching også er ønsket, og ved at deaktivere dem på brugerspecifikke ruter eller holde levetiden kort. På den måde forener jeg ydeevne med krav til databeskyttelse.

Implementeringsdetaljer og præstationsomkostninger

Oprettelsen af en ETag må ikke koste mere end den giver i gevinst. Ved store filer eller ressourcekrævende renderinger gemmer jeg tagget sammen med metadata (filkontrolsum, build-hash, database-rækkeversion) og gentager den ikke ved hver eneste anmodning. Ved sammensatte sider hjælper en Version-Compose-strategi: Jeg sammensætter ETag'en af stabile del-ETag'er (f.eks. skabelon, datafragment, konfiguration), så små ændringer resulterer i en målrettet, men reproducerbar ny værdi. I klynger synkroniserer jeg genereringslogikken i et fælles bibliotek og kontrollerer den i CI, så ingen instans afviger. For ekstremt store blobs satser jeg på hurtige checksummer (CRC64) eller gemmer build-hashes i stedet for at hashe body on-the-fly. Hvor absolut byte-lighed ikke er nødvendig, er det tilstrækkeligt svage ETags som et pragmatisk kompromis.

Almindelige fejl og hvordan du undgår dem

  • Tilfældige ETags: Hvis tags genereres på ny ved hver forespørgsel, er enhver revalidering meningsløs. Jeg sørger for deterministiske værdier, der kun ændres, når der sker en reel ændring.
  • Forkert kombination af direktiver: ingen opbevaring ETag nytter ikke noget – browseren gemmer det alligevel ikke. Jeg vælger ensartede kombinationer for at opnå den ønskede funktion.
  • Overdreven Vary: Variationer i cookie- eller user-agent-strengen ødelægger cachen. Jeg begrænser Vary til reelle ændringer i repræsentationen.
  • Kompressionsfælder: En fælles ETag for gzip og br medfører fejlresultater. Jeg knytter ETags korrekt til den konkrete variant og angiver Vary korrekt.
  • Tidsafvigelse: Unøjagtige serverure forvrider »Last-Modified«-værdien. Jeg holder tidskilderne synkroniserede, så »If-Modified-Since« fungerer korrekt.
  • Forveksling af no-cache: Mange læser det som „ikke cache“. Det, der menes, er „altid revalider“. Til et egentligt forbud bruger jeg ingen opbevaring.

Fejlfinding, målinger og arbejdsgange

For at finde fejlen starter jeg i fanen »Netværk«: Er det korrekt? Cache-kontrol? Opstår ved genoptræning 304 i stedet for 200? Passer ETag og Sidst ændret mellem forespørgsel og svar? Jeg tjekker Varierer, for at se, om varianterne er genkendt korrekt. I logfilerne får jeg vist Hit/Miss-kvoter, 304-procenter og gennemsnitlige responsstørrelser pr. sti. Hvis 304-procenten stiger, falder datamængden og TTFB typisk mærkbart. I belastningstests simulerer jeg gentagne opkald for at måle revalideringsomkostninger i stedet for overførselsomkostninger. Ved afvigelser fjerner jeg gradvist forstyrrende faktorer: Set-Cookie, for strenge Vary-regler, modstridende headere som Pragma. På den måde finder jeg hurtigt den flaskehals, der presser hitraten.

Service Worker som et supplerende cache-lag

Hvis jeg bruger en service worker, bruger jeg den som et ekstra lag, ikke som et modstridende lag. Jeg lader den udføre de samme Cache-kontrol-Respekter signalerne og kombiner strategier som stale-while-revalidate bevidst med HTTP-validering via ETag og Last-Modified. I tilfælde af offline-tilstande kan workeren levere midlertidigt forældede ressourcer og revalidere dem i baggrunden. Det er vigtigt, at den videregiver betingelsesheadere korrekt, ellers mister jeg fordelene ved 304 på netværksstrækningen. På den måde drager også PWA-scenarier fordel af korrekt HTTP-caching i stedet for at omgå dens mekanismer.

SEO-effekt og Core Web Vitals

Forbedre hurtige svar UX og brugersignaler, hvilket forbedrer placeringerne i søgeresultaterne. Især tilbagevendende besøgende drager fordel af dette, da deres browser henter mange filer direkte fra cachen eller via 304-bekræftelser. Denne lavere ventetid har en positiv indvirkning på FCP, LCP og TTFB, som jeg reducerer ved hjælp af målrettet revalidering. Desuden sparer serveren regnetid, som jeg kan bruge til spidsbelastninger eller ressourcekrævende anmodninger. På den måde opretholder jeg ydeevnen, mens indholdet leveres korrekt og rettidigt.

Resumé: Min handlingsplan

Jeg er afhængig af en klar Kombination ud fra Cache-Control, Last-Modified og ETag. Til statiske ressourcer vælger jeg lange gyldighedsperioder og sikrer mig ved hjælp af revalidering, hvis filerne ikke er versionerede. For dynamiske svar genererer jeg pålidelige ETags og holder klynger konsistente. Derefter kontrollerer jeg med værktøjer, metrics og logs, om 304 forekommer ofte nok, og justerer indstillingerne. Sådan sikrer jeg hurtig levering, lavere belastning og en bedre brugeroplevelse gennem effektiv HTTP-caching.

Aktuelle artikler

Illustration af HTTP-betinget caching med ETag og Last-Modified i et webservermiljø
Plesk webserver

Forståelse af HTTP-betinget caching med ETag og Last-Modified

Lær, hvordan HTTP-betinget caching med ETag og Last-Modified fungerer, hvordan validering af browserens cache gennemføres, og hvordan du dermed kan optimere indlæsningstider, båndbredde og serverbelastning.