What Is Storage Fingerprinting? β Definition and How It Works
Learn how storage APIs, quotas, and persistence behaviour identify a browser, and why a pristine profile is itself a signal.
Storage fingerprinting uses the browser's storage mechanisms β their availability, their reported quota, and what they already contain β as identifying and automation-detecting signals.
1. The Surfaces
| Mechanism | Signal it carries |
|---|---|
localStorage |
Availability, existing contents |
sessionStorage |
Availability |
| IndexedDB | Availability, existing databases |
| Cache Storage | Presence of caches |
| Cookies | Contents, and whether they persist |
navigator.storage.estimate() |
Quota and current usage |
navigator.storage.persisted() |
Whether storage is persistent |
2. Quota Reveals the Environment
navigator.storage.estimate() returns a quota derived from available disk space:
const {quota, usage} = await navigator.storage.estimate();
That makes the number an indirect measure of the host. A very small quota suggests a constrained container; an implausibly large one suggests a server with a large volume rather than a consumer laptop.
Combined with usage, it also indicates whether the profile has been used at all β a real browser accumulates cached data, while a fresh automated profile reports near-zero usage.
3. An Empty Profile Is Conspicuous
The counter-intuitive point. Automation typically launches a clean profile every run, so:
localStorageis empty.- IndexedDB contains nothing.
- No caches exist.
- Storage usage is essentially zero.
- No cookies from any prior visit.
A person's browser looks nothing like that. Even a first visit to your site arrives in a browser carrying storage from everywhere else it has been.
So perfect cleanliness is a signal. It is one of the reasons persistent profiles help: a profile reused across runs accumulates the ordinary residue that makes it look used.
4. Storage Availability Varies Meaningfully
Some configurations disable or restrict storage:
- Private browsing restricts persistence and reduces quota.
- Blocked third-party storage changes what an embedded context can do.
- A storage API that throws on write indicates a restricted environment.
function storageWorks() {
try { localStorage.setItem('_t','1'); localStorage.removeItem('_t'); return true; }
catch { return false; }
}
A browser where storage throws is unusual for a normal desktop session, and detection reads it accordingly.
5. Storage as a Tracking Vector
Beyond fingerprinting, storage is used for identity persistence β a value written once and read on return visits. That is what makes clearing it, or not clearing it, a decision with consequences either way:
- Never clearing means a stable identity across your sessions, so anything attributed to one session follows the profile.
- Always clearing produces the pristine-profile signal above, and forfeits any accumulated trust.
The workable middle is one persistent profile per logical identity, reused deliberately, cleaned selectively rather than wholesale.
6. Partitioning Changes the Picture
Modern browsers partition storage by top-level site, so an embedded third party cannot read the same storage across different parent sites. That reduces cross-site tracking through storage β and introduces its own detectable behaviour, since a browser that does share storage across partitions is behaving unusually.
Anything that disables partitioning to make automation simpler is therefore visible.
7. Practical Guidance
- Use persistent profiles, one per identity, so storage looks lived-in.
- Do not wipe storage between every request within a session; that is not what browsers do.
- Let caches accumulate, within reason, and clear them on a schedule to bound growth rather than per run.
- Treat the profile directory as a credential β it holds session state.
- Never share one profile across concurrent browsers; it corrupts, and the state interleaves.
- Expect quota to reflect the host, and provision containers accordingly if the number matters.
8. Summary
Storage fingerprinting reads the availability, contents, and reported quota of localStorage, IndexedDB, caches, and cookies, using them to characterise both the device and the session's history.
The distinctive automation signal is a pristine profile β empty storage, zero usage, no cookies β which no real browser presents, so persistent per-identity profiles that accumulate ordinary residue are both more realistic and operationally simpler than wiping everything each run.