Lagring i webbläsaren
Allt som finns i dina variabler försvinner när användaren laddar om sidan. Ofta gör det ingenting, eftersom datan finns på en server och hämtas igen. Men ibland finns det ingen server, eller så hör datan bara hemma hos just den här användaren: ett val av mörkt tema, ett påbörjat formulär eller topplistan i examinationsuppgift B2. Då behöver webbläsaren själv spara datan.
Den här artikeln ger en översikt över de sätt som finns och vad de har gemensamt. De två följande artiklarna går på djupet med det du behöver för B2, Web Storage, och med alternativet för större mängder data, IndexedDB.
Tidsåtgång: Ungefär 15 minuter.
Vad som finns
Section titled “Vad som finns”Webbläsaren har flera API:er för att lagra data. De skiljer sig i hur mycket de rymmer, vad de kan lagra, om de är synkrona eller asynkrona och hur länge datan ligger kvar:
| API | Lagrar | Rymmer | Anropas | Ligger kvar |
|---|---|---|---|---|
localStorage |
strängar under en nyckel | cirka 5 MiB | synkront | tills den tas bort |
sessionStorage |
strängar under en nyckel | cirka 5 MiB | synkront | tills fliken stängs |
| IndexedDB | objekt, arrayer, datum, filer med mera | gigabyte | asynkront | tills den tas bort |
| Cache API | HTTP-svar, kopplade till sin förfrågan | gigabyte | asynkront | tills den tas bort |
| Cookies | korta strängar, skickas med till servern | cirka 4 KB per cookie | synkront¹ | tills de går ut |
¹ document.cookie är synkront. Det nyare Cookie Store API:et är asynkront.
Det första valet är oftast enkelt. Små mängder data som bara din JavaScript-kod ska läsa, som inställningar och en topplista, sparas i Web Storage. Stora mängder data, eller data du vill söka i, sparas i IndexedDB. Cache API används för att spara svar från nätverket så att en applikation fungerar utan uppkoppling, och hör hemma i progressiva webbapplikationer, som du möter från kursvecka 15. Cookies är till för servern, som du ser längre ner.
Det finns också API:er för att läsa och skriva riktiga filer. Origin Private File System
(navigator.storage.getDirectory()) ger applikationen ett eget filsystem som användaren inte ser,
och fungerar i alla stora webbläsare. Att låta användaren välja en fil på sin egen dator med
window.showOpenFilePicker() fungerar däremot bara i Chromium-baserade webbläsare, som Chrome och
Edge. Inget av dem behövs i kursen.
Allt delas upp per origin
Section titled “Allt delas upp per origin”All lagring i webbläsaren är uppdelad per origin (ursprung), samma begrepp som du mötte i CORS i Nätverksanrop med fetch. En origin är kombinationen av schema, värdnamn och port:
https://gitlab.lnu.se:443└─┬─┘ └─────┬─────┘ └┬┘schema värdnamn portKod på en origin kan bara läsa det som har sparats av samma origin. En sida på https://example.com
kan inte läsa det som https://gitlab.lnu.se har sparat i localStorage, och tvärtom. Det är det som
gör det säkert att spara data i webbläsaren över huvud taget.
Uppdelningen gäller hela origin, inte en enskild sida eller applikation. Alla sidor på samma origin
delar på samma localStorage. Det får en praktisk följd när du utvecklar med Vite:
Varje projekt du startar med npm run dev kör på http://localhost:5173. För webbläsaren är det samma
origin, oavsett vilket projekt det är. Sparar både ditt quiz och ditt memoryspel något under nyckeln
scores, skriver de över varandras data. Därför bör nycklarna ha ett prefix som pekar ut applikationen,
som quiz:scores. Det tar nästa artikel upp.
Fråga: samma dator, samma port
Section titled “Fråga: samma dator, samma port”Du startar ditt quiz med npm run dev, spelar en omgång och ser att topplistan sparas. Dagen efter
öppnar du http://127.0.0.1:5173 i stället för http://localhost:5173. Topplistan är tom. Varför?
localhost och 127.0.0.1 pekar på samma dator, men de är olika värdnamn. Värdnamnet är en del av
origin, så för webbläsaren är det två olika origins med var sin lagring. Topplistan finns kvar, men
under http://localhost:5173.
Av samma skäl har npm run preview, som kör på port 4173, en egen lagring, skild från den du använder
under utvecklingen.
Cookies är till för servern
Section titled “Cookies är till för servern”Cookies är det äldsta sättet att spara data i webbläsaren, och det som skiljer sig mest från de
andra. En cookie skapas oftast av servern, med headern Set-Cookie i ett svar:
HTTP/1.1 200 OKSet-Cookie: sessionId=a3fWa; Max-Age=3600; Secure; HttpOnly; SameSite=LaxWebbläsaren sparar cookien och skickar den sedan med i varje förfrågan till samma webbplats, i
headern Cookie. Det är så en server känner igen dig när du är inloggad: varje förfrågan bär med sig
samma sessionId.
Attributen efter värdet styr hur cookien hanteras:
Max-AgeellerExpires– hur länge cookien gäller. Utan dem är det en sessionscookie, som försvinner när webbläsaren stängs.Secure– skickas bara över HTTPS.HttpOnly– går inte att läsa från JavaScript. Det skyddar till exempel en inloggning från ett skript som har smugits in på sidan.SameSite– om cookien ska skickas med när förfrågan kommer från en annan webbplats.
JavaScript kan läsa och skriva cookies med document.cookie, men gränssnittet är egendomligt.
Att läsa ger alla cookies (utom de med HttpOnly) i en enda sträng, och att tilldela lägger till eller
ersätter en cookie, inte alla:
console.log(document.cookie) // "theme=dark; lang=sv"
document.cookie = 'lang=en; Max-Age=31536000; SameSite=Lax'
console.log(document.cookie) // "theme=dark; lang=en"Det nyare Cookie Store API:et
(await cookieStore.get('lang')) är asynkront och lättare att använda, och fungerar i alla stora
webbläsare sedan 2025. Men för det mesta du vill spara från JavaScript är cookies fel verktyg. Varje
cookie skickas med i varje förfrågan, också förfrågningar efter bilder och stilmallar, och servern har
ingen nytta av ditt val av tema eller din topplista. Webbläsarna behöver bara klara 4 096 byte per
cookie och 50 cookies per webbplats enligt standarden, så mer än så går inte att lita på. Spara
sådant i Web Storage i stället.
Fråga: varför syns inte inloggningen?
Section titled “Fråga: varför syns inte inloggningen?”Du är inloggad på en webbplats, och i utvecklarverktygen ser du en cookie sessionId. Men
document.cookie i konsolen visar bara "theme=dark". Var är sessionId?
Den har attributet HttpOnly. Den skickas med i förfrågningarna till servern som vanligt, men den syns
inte för JavaScript, varken via document.cookie eller Cookie Store API:et. Det är meningen: ett
skript som någon har lyckats smyga in på sidan ska inte kunna läsa och skicka iväg din inloggning.
Utvecklarverktygen visar alla cookies, eftersom de inte är en del av sidan.
Hur länge ligger datan kvar?
Section titled “Hur länge ligger datan kvar?”Det mesta som sparas i localStorage, IndexedDB och Cache API ligger kvar tills någon tar bort det.
Men “någon” är inte bara din kod:
- Användaren kan när som helst rensa webbplatsens data i webbläsarens inställningar, använda ett privat fönster, där allt försvinner när fönstret stängs, eller byta till en annan webbläsare eller dator.
- Webbläsaren rensar själv när disken börjar bli full. Den tar då bort all data för den origin som har använts minst nyligen, först.
- Safari tar bort data som skript har sparat för en webbplats som användaren inte har besökt och klickat på under sju dagars användning av webbläsaren.
Data i webbläsaren är alltså aldrig helt säker. Den passar för sådant som är bekvämt att ha kvar, och som går att leva utan om det försvinner. En topplista i ett quiz är ett bra exempel. Användarens enda kopia av en uppsats är det inte; den hör hemma på en server, eller i en fil som användaren själv sparar.
Utrymme och kvoter
Section titled “Utrymme och kvoter”Varje origin har en kvot, ett tak för hur mycket den får lagra. För Web Storage är taket cirka 5 MiB per origin i alla stora webbläsare. För IndexedDB och Cache API är det mycket större, upp till en betydande del av diskens storlek, och varierar mellan webbläsarna.
Hur mycket som används och hur mycket som finns kvar kan du fråga efter med Storage Manager API:et:
const { usage, quota } = await navigator.storage.estimate()
console.log(`Used ${(usage / 1024 / 1024).toFixed(1)} MiB of ${(quota / 1024 / 1024).toFixed(0)} MiB`)Båda värdena är i byte, och de är uppskattningar. Försöker du spara mer än kvoten tillåter kastas ett
QuotaExceededError, både från localStorage och från IndexedDB.
En applikation kan också be om att få sin data skyddad mot webbläsarens egen rensning, med
navigator.storage.persist(). Firefox frågar användaren, medan Chrome och Safari själva avgör efter
hur mycket användaren har använt webbplatsen. Användaren kan fortfarande rensa allt själv.
Lagen om lagring hos användaren
Section titled “Lagen om lagring hos användaren”Den kända cookierutan på nästan varje webbplats har sin grund i en lag, inte i tekniken. I Sverige står det i lagen (2022:482) om elektronisk kommunikation, 9 kap. 28 §, som bygger på ett EU-direktiv. Kortfattat får uppgifter lagras i eller hämtas från användarens dator eller telefon bara om användaren har fått veta vad de används till och har samtyckt. Det finns två undantag: lagring som behövs för att överföra ett meddelande, och lagring som är nödvändig för en tjänst som användaren uttryckligen har bett om.
Två saker är värda att lägga märke till:
- Lagen gäller all lagring, inte bara cookies. Den talar om att lagra uppgifter i användarens
utrustning, oavsett teknik.
localStorageoch IndexedDB omfattas på samma sätt. - Allt kräver inte samtycke. En kundvagn, en inloggning eller ett val av tema som användaren själv har gjort är nödvändiga för det användaren har bett om. Det är framför allt spårning och statistik som kräver samtycke.
Det här är en orientering, inte juridisk rådgivning. I Sverige är det Post- och telestyrelsen, PTS, som har tillsyn över reglerna.
Fråga: behöver topplistan en cookieruta?
Section titled “Fråga: behöver topplistan en cookieruta?”Ditt quiz sparar spelarnas smeknamn och tider i localStorage, för att kunna visa en topplista på
samma dator. Inget skickas till någon server. Behöver du be om samtycke?
Troligen inte. Användaren spelar quizet och förväntar sig att se topplistan; att spara resultaten är en del av det användaren har bett om, och datan lämnar aldrig datorn.
Det ändras om du lägger till något som användaren inte har bett om. Skickar du resultaten till en tjänst för statistik, eller sparar en identitet för att känna igen användaren mellan besöken, är det inte längre nödvändigt för quizet.
Se det själv i utvecklarverktygen
Section titled “Se det själv i utvecklarverktygen”Allt som har sparats för en origin syns i utvecklarverktygen: under fliken Application i Chrome
och Edge, och Storage i Firefox och Safari. Där finns localStorage, sessionStorage,
IndexedDB, Cache Storage och cookies, var för sig. Du kan läsa, ändra och ta bort enskilda poster,
och rensa allt för webbplatsen med ett klick. Det är praktiskt när du vill se vad din kod faktiskt har
sparat, eller testa hur applikationen beter sig första gången den körs.
Att ta med sig
Section titled “Att ta med sig”- Små mängder data som bara JavaScript ska läsa sparas i Web Storage. Stora mängder, eller strukturerad data, sparas i IndexedDB.
- Cookies skickas med i varje förfrågan och är till för servern. Använd dem inte för data som bara din kod behöver.
- All lagring är uppdelad per origin: schema, värdnamn och port. Alla applikationer på samma origin,
som alla Vite-projekt på
localhost:5173, delar på samma lagring. - Data i webbläsaren kan försvinna. Spara bara sådant som det går att leva utan.
- Varje origin har en kvot. Är den full kastas ett
QuotaExceededError. - Lagen om samtycke gäller all lagring hos användaren, men inte det som är nödvändigt för det användaren har bett om.
Vidare läsning
Section titled “Vidare läsning”- Läsanvisningarna för veckan, kapitel 15.12 i kursboken.
- Client-side storage och Storage quotas and eviction criteria på MDN.
- Using HTTP cookies på MDN.
- Kakor (cookies) hos Post- och telestyrelsen.