Cookies, Sessions and Storage
How websites remember you with cookies, sessions, localStorage and sessionStorage, what consent banners must really do, and how to test login state, expiry, carts and privacy with DevTools.
A customer checks their bank balance on a library computer, clicks Log out and walks away. The next person opens the same browser, presses Back, then Reload… and lands in the first customer’s account.
The logout button “worked”: it showed the login page. But the login token was still sitting in the browser’s storage, and the app happily used it again. Nobody noticed in testing, because nobody looked at what the browser was remembering.
That’s what this lesson is about. Websites keep a surprising amount of data in your browser: cookiesCookieA small piece of data a website stores in your browser, for example to keep you logged in.Open in glossary →, local storageLocal storageBrowser storage where websites keep data that survives closing the tab, like settings or a cart.Open in glossary →, session storage and more. Testers who know where to look find bugs in privacy, security and “it forgot my cart” problems that everyone else misses.
Why it matters
- Security: a session that doesn’t end on logout, or a token readable by any script, can hand someone else your account.
- Privacy and law: in the EU and UK, tracking cookies need consent. A “Reject” button that doesn’t actually reject is a legal risk, not just a bug.
- Money: a cart that disappears after login, or a checkout that logs you out halfway, loses sales.
- Trust: seeing someone else’s cart or name on a shared computer is the kind of bug that ends up in the news.
Where a website keeps data
| Storage | Lifetime | Sent to the server automatically? | Readable by page JavaScript? | Typical use |
|---|---|---|---|---|
| Cookie | Until it expires, or until the browser closes (session cookie) | Yes, with every request to its domain | Yes, unless HttpOnly | Login session, consent choice, language, analytics IDs |
| localStorage | Forever, until the site or user deletes it | No | Yes | Guest cart, theme, “don’t show again” flags |
| sessionStorage | Until the tab is closed (survives reloads) | No | Yes | Form drafts, one-time banners, checkout steps in one tab |
| IndexedDB | Forever, until deleted | No | Yes | Large structured data: offline mode, cached messages, files |
| Cache Storage / HTTP cache | Until evicted or cleared | (It replaces requests) | Cache Storage: yes | Offline pages (service workers), images, scripts |
The sizes differ too: a cookie holds about 4 KB, localStorage and sessionStorage about 5 MB per site, and IndexedDB can hold far more. You don’t need to memorize numbers, but you should know which one a feature uses, because that decides what survives a reload, a closed tab or a logout.
Cookies up close
A server creates a cookie by sending a Set-Cookie header in its response. The browser stores it and sends it back in a Cookie header with every following request to that site.
Response header from the server
Set-Cookie: session_id=s_9f2c41e7; Domain=shop.example; Path=/; Max-Age=2592000; HttpOnly; Secure; SameSite=Lax
Name=value the data itself
Domain, Path where it’s sent
Expires / Max-Age how long it lives
HttpOnly, Secure, SameSite security flags
| Attribute | What it does | What to check |
|---|---|---|
| Name / Value | The data, e.g. session_id=s_9f2c… | No readable personal data (email, name) or secrets in plain text |
| Domain | Which hosts receive it. Domain=shop.example also includes subdomains like api.shop.example; without a Domain, only the exact host gets it | Not wider than needed |
| Path | Which URLs receive it, usually / | Admin cookies limited to admin paths if intended |
| Expires / Max-Age | When it’s deleted. No expiry = session cookie | Matches the requirement (“Remember me” for 30 days, otherwise session) |
| HttpOnly | Page JavaScript can’t read it (document.cookie won’t show it) | Must be set on session/auth cookies, so Cross-site scripting (XSS)Cross-site scripting (XSS)An attack where text a user enters is run as code in other people's browsers.Open in glossary → can’t steal them |
| Secure | Only sent over HTTPSHTTPSThe secure version of HTTP; traffic between the browser or app and the server is encrypted.Open in glossary → | Must be set on anything sensitive |
| SameSite | Whether it’s sent on requests coming from other sites: Strict, Lax or None | Lax or Strict for sessions. None requires Secure. If SameSite is missing, Chromium browsers (Chrome, Edge) treat the cookie as Lax |
Session cookies vs persistent cookies
- A session cookie has no
ExpiresorMax-Age. The browser deletes it when it closes. In DevTools its expiry shows as Session. - A persistent cookie has an expiry date and survives restarts. “Remember me”, language and consent cookies are usually persistent. Chrome caps cookie lifetimes at 400 days.
First-party vs third-party cookies
- First-party: set by the site in the address bar (
shop.example). Login, cart and consent cookies. - Third-party: set by another domain embedded in the page, e.g. an ad network’s
IDEcookie fromadnet.example. Used mostly for tracking across sites.
Browsers increasingly restrict third-party cookies. Safari blocks them by default, Firefox isolates them per site, and Chrome blocks them in Incognito windows and lets users block them everywhere. For testers this means: features that rely on third-party cookies (embedded logins, chat widgets, payment iframes) must be tested in Safari, Firefox and Incognito, or they’ll break for real users. See Cross-browser Testing.
Sessions: how “staying logged in” works
HTTP itself has no memory. Every request is a stranger. A sessionSessionThe period during which a user stays logged in without entering their credentials again.Open in glossary → is how the server recognizes you again:
- You log in. The server checks your password and creates a session record: “session
s_9f2cbelongs to Anna, last active 09:00”. - The server sends a session tokenSession tokenA string the server issues after login that proves who you are on later requests.Open in glossary → in a
Set-Cookieheader. The token is just a long random ID, not your password. - The browser sends the cookie with every request. The server looks up the session and knows it’s Anna.
- The session expires after a period of inactivity (an idle timeout, e.g. 30 minutes for a bank) and/or a maximum age (an absolute timeout, e.g. 12 hours). The server must reject the old token afterwards.
- Logout destroys the session on the server and tells the browser to delete the cookie. Even a copied token should stop working.
Some apps use a token (often a JWT) stored in localStorage instead of a cookie and send it in an Authorization header. That works, but any script on the page, including an injected one, can read localStorage. So check extra carefully that the token is removed on logout and expires on its own.
“Remember me” usually means a persistent cookie with a long expiry (for example 30 days) instead of a session cookie. When it’s unchecked, closing the browser should log you out.
localStorage, sessionStorage, IndexedDB and cache
| Event | Cookies (session) | Cookies (persistent) | localStorage | sessionStorage |
|---|---|---|---|---|
| Reload the page | Kept | Kept | Kept | Kept |
| Open the site in a new tab | Shared | Shared | Shared | New, empty |
| Close and reopen the tab | Kept | Kept | Kept | Gone |
| Quit and reopen the browser | Gone | Kept until expiry | Kept | Gone |
| Private / Incognito window | Separate, wiped on close | Separate, wiped on close | Separate, wiped on close | Separate |
| DevTools › Clear site data | Gone | Gone | Gone | Gone |
| User clicks Log out | Only what the app deletes | Only what the app deletes | Only what the app deletes | Only what the app deletes |
(Reopening a closed tab with Ctrl/Cmd+Shift+T, or duplicating a tab, can bring sessionStorage back, so close tabs normally when you test this.)
The last row is the important one. Logout clears nothing automatically. The app must delete every piece of personal data itself. That’s where the bugs are.
IndexedDB is a real database inside the browser, used by apps that work offline (email, notes, chat). Test it like localStorage: is it cleared on logout? Does it hold sensitive data? Cache Storage (used by service workers) can serve an old version of a page after a release, a classic “the fix doesn’t show up” problem. Clear site data removes all of these at once.
Consent banners: GDPR basics for testers
In the EU, the ePrivacy rules require consent before storing non-essential cookies (analytics, advertising, social media), and the GDPR defines what valid consent is. The UK has similar rules (PECR), and since 2026 allows some simple first-party analytics cookies without consent as long as users can opt out, so check which rules your product follows. You don’t need to be a lawyer, but you should test these points:
- Nothing optional before a choice. Before you click anything, only strictly necessary cookies may exist (session, cart, security, the consent choice itself). No
_ga, no ad cookies, no requests to analytics domains. - Reject is as easy as Accept. A “Reject all” button on the first layer, equally visible. Not hidden behind “Settings” in grey text.
- Reject really rejects. After rejecting, no analytics cookies appear, and no analytics requests show up in the Network panel, on this page or the next ones.
- Accept really accepts. The expected cookies appear, and only after the click.
- The choice is remembered (usually in a consent cookie), and the banner doesn’t reappear on every page.
- Consent can be withdrawn as easily as it was given (a “Cookie settings” link), and withdrawing deletes the cookies or at least stops new tracking.
- Granular choices work. If there are toggles per category, each one is respected.
Try it: a shop and its Application panel
Pantry is a small shop with DevTools’ Application panel next to it. Everything you do writes to cookies, localStorage or sessionStorage, and changed rows flash yellow. Use the browser controls to close the tab, quit the browser or let time pass.
- Olive oil, 750 ml€8.90
- Coffee beans, 1 kg€11.50
- Fusilli pasta, 500 g€1.80
Your cart
Empty
We value your privacy
We use necessary cookies to run the shop. With your OK we also use analytics and advertising cookies.
No cookies for this site.
Select a row to see its full value.
Browser controls
Tue 13 Oct, 09:00
- → Page loaded. No cookies yet: the consent banner is waiting for your choice.
How to inspect and edit storage in real DevTools
New to DevTools? Start with DevTools for Testers.
- Open DevTools (F12 / Cmd+Option+I) and go to the Application panel.
- In the sidebar under Storage, expand Cookies, Local storage or Session storage and click the site’s origin.
- Read the table: name, value, domain, path, Expires / Max-Age, size, HttpOnly, Secure, SameSite. Cookies with problems are highlighted, and hovering shows why.
- Edit a value by double-clicking it. Delete a row by selecting it and pressing Delete, or use the 🚫 button to clear the whole list.
- Clear everything: Application › Storage › Clear site data.
- To see which cookies a request sent or received, open the request in Network and check its Cookies tab. Blocked cookies are shown with the reason.
- Open DevTools (F12) and go to the Storage panel.
- Expand Cookies, Local Storage, Session Storage, Indexed DB or Cache Storage and pick the site.
- Double-click a value to edit it. Right-click a row to delete it, or delete everything for the site.
- The sidebar on the right parses values, which is handy for JSON in localStorage.
- Enable developer features (Safari › Settings › Advanced › Show features for web developers) and open Web Inspector with Cmd+Option+I.
- Open the Storage tab and choose Cookies, Local Storage, Session Storage or IndexedDB.
- Double-click values to edit, and use the delete key or the trash button to remove entries.
- Remember that Safari blocks third-party cookies and limits some script-written storage, so features can behave differently than in Chrome.
The Console is useful too:
document.cookielists the cookies JavaScript can read. If you see the session cookie here, it’s missingHttpOnly. That’s a bug.localStorage.getItem('cart')shows one value.Object.keys(localStorage)lists every key.localStorage.clear()andsessionStorage.clear()wipe them for this site.
What to test
Login and logout
- Logging in sets a session cookie with
HttpOnly,SecureandSameSite. Nothing secret appears in localStorage or the URL. - Logging out deletes the session cookie and any tokens or personal data in localStorage, sessionStorage and IndexedDB.
- After logout, Back and Reload don’t show account pages. A copied old session cookie no longer works.
Session expiry and “Remember me”
- After the idle timeout, the next action asks you to log in again, with a clear message and without losing what you typed if possible.
- “Remember me” off: quitting the browser logs you out (session cookie). “Remember me” on: you stay logged in until the stated expiry, then you’re logged out.
- The session also expires on the server: deleting or editing the cookie mid-session leads to a clean “Please log in” state, not a crash or a blank page.
Multiple tabs and windows
- Logging out in one tab logs you out in others on their next action.
- Logging in as another user in a second tab doesn’t mix data between the two.
- A private / Incognito window starts clean and keeps nothing after closing.
Cart and preferences
- The guest cart survives a reload and a browser restart (if the requirement says so).
- Logging in merges or replaces the guest cart as specified. Logging out doesn’t leave the cart for the next person on the same browser.
- Language, theme and “don’t show again” choices persist where expected, and nowhere else.
Consent and privacy
- No optional cookies or analytics requests before consent. Reject works. Accept works. Withdrawing works.
- No personal or sensitive data in localStorage (email, address, tokens, card details).
- Clearing cookies mid-session, or blocking cookies entirely in browser settings, produces a helpful message, not a broken site.
Common bugs
| # | Bug | What you see | Typical severity |
|---|---|---|---|
| 1 | Logout doesn’t clear the token | Token stays in localStorage; Reload or Back logs you back in | Critical |
| 2 | Session never expires | Still logged in after hours of inactivity; server accepts old tokens | High |
| 3 | Session cookie missing HttpOnly / Secure | document.cookie shows the session ID; no ✓ in the HttpOnly/Secure columns | High |
| 4 | “Remember me” ignored | Cookie has an expiry date even when unchecked, or none when checked | Medium–High |
| 5 | Reject doesn’t reject | Analytics cookies and requests appear after “Reject all” | High (legal) |
| 6 | Cookies set before consent | _ga or ad cookies exist before you click the banner | High (legal) |
| 7 | Data leaks between users | Previous user’s cart, name or address shown after logout/login on the same browser | High–Critical |
| 8 | Sensitive data in web storage | Email, address, tokens or card data in localStorage | High |
| 9 | Crash when a cookie is missing | Deleting a cookie gives a blank page or endless spinner instead of “Please log in” | Medium |
| 10 | Cart lost on login | Guest cart disappears when the user logs in | Medium |
Find the bug
Here’s another build of Pantry, with six bugs. Watch the Application panel closely after every action, and use the browser controls.
- Make a cookie choice, then look at the Cookies table.
- Log in (with and without “Remember me”), then log out and reload.
- Check the flags on
session_id. - Quit the browser, wait, and log in as a different user.
🐞 Bug hunt: Pantry shop storage
This demo has 6 hidden bugs. Explore it like a tester, then check your findings.
- Olive oil, 750 ml€8.90
- Coffee beans, 1 kg€11.50
- Fusilli pasta, 500 g€1.80
Your cart
Empty
We value your privacy
We use necessary cookies to run the shop. With your OK we also use analytics and advertising cookies.
No cookies for this site.
Select a row to see its full value.
Browser controls
Tue 13 Oct, 09:00
- → Page loaded. No cookies yet: the consent banner is waiting for your choice.
- 1
Hint: Click Reject all and look at the Cookies table.
- 2
Hint: Log in, log out, check Local storage, then press Reload.
- 3
Hint: Log in and select session_id in the Cookies table.
- 4
Hint: Log in with Remember me unchecked, look at the Expires column, then Quit & reopen browser.
- 5
Hint: Log in as Anna, add items, log out, then log in as Ben.
- 6
Hint: Log in without Remember me, delete auth_token, then use Wait 31 min.
More security checks like these are in Security Basics for Testers, and the full login flow is covered in Login and Sign Up.
Example test cases
TC-ST-001Logout removes the session and all auth datapositivehigh
Preconditions
- User anna@test.com exists
- DevTools open on Application
Steps
- Log in as anna@test.com
- Click Log out
- Check Cookies, Local storage and Session storage
- Reload the page
- Press Back
Expected result
No session cookie, token or personal data remains. After reload and Back the page shows the guest view.
TC-ST-002Session cookie has secure flagspositivehigh
Steps
- Log in
- Open Application › Cookies and select session_id
- In the Console, run document.cookie
Expected result
session_id has HttpOnly and Secure ticked and SameSite=Lax or Strict. It does not appear in the document.cookie output.
TC-ST-003Without "Remember me", quitting the browser ends the sessionpositivehigh
Steps
- Log in with Remember me unchecked
- Check that session_id expires at "Session"
- Quit the browser completely and reopen the site
Expected result
The user is logged out and sees the login form.
TC-ST-004Idle session expires after 30 minutesedgehigh
Preconditions
- Idle timeout configured to 30 minutes (ask devs for a shorter timeout on the test environment)
Steps
- Log in without Remember me
- Do nothing for 31 minutes
- Add a product to the cart
Expected result
The request returns 401. The page shows "Your session expired. Please log in again." The product is not lost.
TC-ST-005Rejecting cookies sets no analytics cookies or requestsnegativehigh
Preconditions
- Fresh Incognito window
- DevTools open on Application and Network
Steps
- Open the site and check cookies before clicking anything
- Click Reject all
- Browse two more pages
Expected result
Before and after rejecting, only necessary cookies exist (plus cookie_consent). No requests go to analytics or ad domains.
TC-ST-006Next user doesn't see the previous user's cartnegativehigh
Steps
- Log in as anna@test.com and add 2 items
- Log out
- Log in as ben@test.com
Expected result
Ben sees only his own cart. Anna's items are not shown and not in localStorage.
TC-ST-007Deleting the session cookie mid-session is handled gracefullyedgemedium
Steps
- Log in
- Delete session_id in Application › Cookies
- Add a product to the cart
Expected result
The app shows a "Please log in again" message. No crash, blank page or endless spinner.
Tip: click a test case to expand it.
Checklist
Cookies, sessions and storage checklist
0 of 15 checked
Check your understanding
Quick quiz
0/5 answered · 0 correct
1.In DevTools, a cookie shows "Session" in the Expires / Max-Age column. What does that mean?
2.Which flag stops page JavaScript, including injected scripts, from reading a cookie?
3.A user dismisses a banner, the site saves that in sessionStorage. When will the banner come back?
4.After clicking "Reject all" on the consent banner, you see a _ga cookie in the Application panel. What is this?
5.You log out, then reload, and you are logged in again. Where should you look first?
Key takeaways
- Cookies travel to the server with every request. localStorage and sessionStorage stay in the browser. sessionStorage dies with the tab.
- Session cookies have no expiry and vanish when the browser closes. Persistent cookies live until their date.
- Auth cookies need HttpOnly, Secure and SameSite. Tokens and personal data don’t belong in localStorage.
- Logout clears nothing by itself. The app must end the server session and delete every token and personal item in the browser.
- Test expiry, “Remember me”, multiple tabs, private windows, deleting cookies mid-session and switching users on one browser.
- Consent banners: nothing optional before a choice, Reject as easy as Accept, and Reject must really reject. Verify it in the Application and Network panels.
Finished this page?Nice work, this page is complete!
Mark it as done to track your progress. It's saved in this browser, no account needed.
Its review cards are now in Bites ⚡