Beyond cookies: localStorage, IndexedDB, ETags and the full client-side tracking surface
When people talk about "deleting cookies" they imagine they have cleared a tracker's memory of them. In reality a browser exposes at least a dozen places where a 32-character identifier can be hidden, and several of them survive the exact cleanup most people run. This article maps every client-side storage and persistence vector a tracker can abuse, explains why "clear cookies" misses most of them, and gives you concrete, tested ways to wipe or isolate the whole surface.
Why cookies are the least of your problems
A tracking identifier only needs to do one thing: persist across page loads and survive your attempts to remove it. A cookie is the obvious, oldest container for that, but it is also the easiest to clear and the most heavily regulated. So trackers spread the same identifier across many containers at once. If you wipe one but not the others, the surviving copy is read back and the cookie is silently rewritten. This respawning technique is the core of what researchers named the evercookie.
The defensive lesson is blunt: clearing one store does nothing unless you clear all of them in the same pass. Below is the full list.
The scriptable storage tier
First- and third-party cookies
A first-party cookie is set by the site in your address bar. A third-party cookie is set by a domain embedded in the page, such as an ad network's pixel, and historically the same network's cookie was readable across every site that embedded it, producing cross-site browsing histories. As of 2026 third-party cookies are blocked by default in Safari and Firefox and are partitioned or being removed in Chromium. They are not gone everywhere, and first-party cookies remain fully functional and are increasingly used to smuggle third-party IDs via CNAME cloaking, where a subdomain like metrics.example.com secretly resolves to a tracker.
localStorage and sessionStorage
The Web Storage API gives every origin a key-value store. localStorage persists with no expiry until explicitly deleted; sessionStorage lasts only for the tab session. Critically, neither is touched when a user clears "cookies" in most quick-clear dialogs, and a value like localStorage.setItem('uid', '8f3a...') is trivially read back to respawn a deleted cookie. Typical capacity is around 5-10 MB per origin, vastly more than a cookie's 4 KB.
IndexedDB
IndexedDB is a full transactional database in the browser, often hundreds of megabytes. It was designed for offline web apps but is equally happy storing a single tracking record. Because it is asynchronous and structured, casual users never inspect it, and many "clear browsing data" presets historically left it intact unless you selected the cookies-and-site-data option explicitly.
The cache tier: storage that does not look like storage
The Cache API and service workers
A service worker can install itself and use the Cache API to store responses keyed by URL. An attacker can encode an identifier into the set of cached URLs or into a cached response body. The service worker then intercepts future requests and reads the value back, even working partly offline. This is persistence that lives entirely outside anything labelled "cookies."
ETag and Last-Modified revalidation
This vector needs no script at all. When a browser caches a resource, the server can attach an ETag header, an opaque string meant to identify a version of the file. On the next visit the browser sends If-None-Match: "the-etag" to ask whether its copy is current. A tracker simply sets the ETag of a 1x1 image to your unique ID. Every time the browser revalidates that image, it hands your ID straight back. Last-Modified works the same way via the If-Modified-Since request. These are HTTP cache mechanisms, so clearing cookies and even local storage leaves them untouched; only clearing the cache removes them.
HSTS supercookies
HTTP Strict Transport Security lets a site tell the browser "always use HTTPS for me." The browser remembers this per domain. A tracker controlling many subdomains (bit0.tracker.com, bit1.tracker.com, and so on) can set HSTS on a chosen subset, encoding an identifier in binary: each subdomain that gets an instant HTTPS redirect is a 1, each that does not is a 0. Reading 32 subdomains reconstructs a 32-bit ID. This abuses a security feature as a storage medium, which is why it is hard to clear without also clearing the HSTS list.
Favicon cache tracking
Browsers keep a separate, aggressively persistent cache for favicons. The 2021 "Supercookie" research showed a server can record which favicon paths a browser does and does not request, encoding an identifier across many favicon entries. The favicon cache was notably resistant to normal clearing and even to some private-browsing boundaries when first disclosed, making it one of the more durable vectors browsers had to specifically harden against.
How respawning ties it together
Imagine a tracker writes ID 8f3a... into a cookie, localStorage, IndexedDB, a service-worker cache, and an ETag at the same time. You open the quick-clear dialog and tick "cookies." The cookie dies. On your next visit, the script reads localStorage, finds the ID still present, and rewrites the cookie. You now look like a returning user despite having "cleared" everything you were offered. That is the evercookie pattern, and it is why partial clearing is close to useless against a determined tracker.
Our privacyscore.dev test probes several of these vectors directly and writes a marker, then checks whether your browser hands it back, so the resulting score reflects respawn resistance rather than just cookie state.
Clearing the whole surface, not one corner
- Use the all-encompassing option. In Chromium choose Clear browsing data and select Cookies and other site data plus Cached images and files; the first covers localStorage, IndexedDB, service workers and the Cache API, the second covers ETag/Last-Modified entries. In Firefox the equivalent is Cookies and Site Data plus Cached Web Content.
- Clear HSTS deliberately. Standard clears do not always purge the HSTS list. In Chromium you can reset entries at
chrome://net-internals/#hsts; in Firefox a full Site Data clear handles it. - Treat the cache as storage. Never clear cookies without also clearing the cache in the same action, or ETag identifiers survive.
Better than clearing: never sharing the surface in the first place
Storage partitioning (state partitioning)
Modern Firefox and Safari, and Chromium's storage partitioning, key every storage vector, cookies, localStorage, IndexedDB, caches, even HSTS, by the top-level site you are visiting, not just by the embedded origin. A tracker embedded on news.example and on shop.example gets two completely separate buckets, so the identifier it writes on one site is invisible on the other. This neutralises cross-site respawning without you clearing anything, and it is the single most important defence to confirm is enabled.
Container tabs and profiles
Firefox Multi-Account Containers give each container its own isolated cookie jar and storage. Using a dedicated container for a logged-in account keeps its identifiers from bleeding into general browsing. Separate browser profiles achieve the same with a harder wall.
Clear on close
Set the browser to delete all site data when it closes (Firefox: Delete cookies and site data when closed; Brave and Chromium have equivalents). Combined with partitioning, this caps the lifetime of any identifier to a single session.
Block service workers and storage where you do not need them
Extensions and built-in settings can block third-party storage entirely, and disabling service workers for sites you do not use offline removes the Cache API persistence vector for them.
A realistic posture
You will not manually inspect IndexedDB before every browse, and you should not have to. The durable strategy is structural: a browser with storage partitioning on by default, third-party cookies blocked, site data cleared on close, and sensitive logins isolated in containers. That converts the entire list above from a dozen independent leaks into a per-session, per-site sandbox. Run the storage and respawn checks on privacyscore.dev after changing these settings; if your score on the persistence tests jumps, the partitioning is doing its job. Cookies were never the boundary, the storage architecture is, and once you treat it that way the cleanup problem mostly disappears.