Back to Blog
Fingerprint

Storage Quota Fingerprinting: How Disk Size Reveals Your Identity

How StorageManager.estimate() exposes disk size as a tracking signal, and how to control storage quota responses at the browser engine level.

Introduction

The Storage Manager API was introduced to help web applications understand how much storage space is available for their data. Through navigator.storage.estimate(), a website can query the approximate storage quota (maximum available space) and current usage (how much has been consumed). This information helps applications make decisions about caching strategies, offline data storage, and whether to prompt users to free up space.

The API was designed with good intentions. Progressive web apps, offline-first applications, and media-heavy sites all benefit from knowing their storage budget. However, the quota value returned by navigator.storage.estimate() is derived from the device's actual disk characteristics. Chrome, for example, typically allocates a quota based on a percentage of the total available disk space. This means the returned value indirectly reveals information about the user's physical storage hardware, creating a fingerprinting signal that persists across sessions and survives common privacy measures.

Privacy Impact

Storage quota fingerprinting is concerning because disk configuration is a stable, hardware-specific characteristic. Unlike cookies or session data, the available disk space on a device changes slowly (only when files are added, removed, or when a drive is replaced). This means the storage quota value is a persistent identifier.

The privacy implications are significant:

  • Disk size identification: A device with a 256 GB SSD produces a different quota value than one with a 512 GB or 1 TB drive. Since disk sizes follow standard manufacturing tiers (128, 256, 512, 1024 GB), the quota value effectively reveals the storage tier.
  • Usage pattern inference: The ratio between quota and usage reveals how full the disk is. A nearly full disk (high usage relative to quota) behaves differently from a freshly installed system. Over time, changes in this ratio can reveal user activity patterns.
  • Operating system identification: Different operating systems calculate storage quotas differently. Chrome on Windows, macOS, and Linux each apply different formulas and caps, meaning the same physical disk produces different quota values depending on the OS.
  • Device class identification: A device with a 2 TB drive is likely a desktop or high-end laptop, while 64 GB suggests a budget laptop or Chromebook. This device class information supplements other hardware signals.

Research from the University of Adelaide demonstrated that storage quota values, when combined with other storage-related signals (IndexedDB availability, Cache API behavior, persistent storage permission status), could increase fingerprint uniqueness by 5-10%. The API requires no permissions and produces no notifications.

The problem is compounded by the fact that the quota value is not static per device. It changes as disk space is used and freed, creating a slowly evolving identifier that can be tracked over time. A user whose quota decreases by 20 GB over a month is leaving a trail of storage state that can be correlated with other signals.

Technical Background

How navigator.storage.estimate() Works

The navigator.storage.estimate() method returns a promise that resolves to an object with two properties:

  • quota: The total storage space available to the origin, in bytes. This is typically a percentage of the total disk space, subject to browser-specific caps and policies.
  • usage: The amount of storage space currently consumed by the origin, in bytes. This includes data in IndexedDB, Cache API, service worker registrations, and other storage mechanisms.

Chrome calculates the quota as roughly 60% of the total available disk space, with additional per-origin limits. Firefox uses a different formula based on available free space. Safari imposes stricter per-origin limits with a prompt-based increase mechanism.

Quota Calculation Details

On Chrome (and Chromium-based browsers), the storage quota calculation follows this approximate logic:

  1. Determine the total volume size of the disk partition containing the profile directory
  2. Calculate available free space on that partition
  3. Apply a percentage-based formula (approximately 60% of total for temporary storage, with a pool shared across origins)
  4. Apply per-origin caps to prevent any single origin from consuming the entire quota

The result is that two machines with different disk sizes produce measurably different quota values, even when both are running the same browser version on the same operating system.

IndexedDB and Cache API Signals

Beyond navigator.storage.estimate(), other storage APIs can leak disk information:

  • IndexedDB: The maximum database size is influenced by the storage quota. Attempting to store increasingly large blobs can reveal the approximate quota through success/failure boundaries.
  • Cache API: Similar to IndexedDB, the Cache API shares the same storage quota, and attempting to cache large responses reveals quota limits.
  • Persistent storage: The navigator.storage.persist() method requests persistent storage (not subject to eviction). Whether this succeeds and how it affects the quota is platform-dependent.

Value Precision

The quota value is returned in bytes, providing very high precision. A 512 GB drive might produce a quota of 307,200,000,000 bytes (approximately 60% of 512 GB), while a 256 GB drive produces roughly 153,600,000,000. The exact value depends on actual free space, other profiles, and OS overhead, but the order of magnitude reliably distinguishes disk sizes.

This precision means that even small differences in disk configuration, such as different partition layouts or different amounts of used space, produce distinguishable quota values.

Cross-Browser Differences

Different browsers calculate quotas differently:

  • Chrome: Percentage of total disk space with per-origin limits
  • Firefox: Based on available free space, with different tier calculations
  • Safari: Fixed per-origin limits (typically 1 GB initially, expandable with user permission)
  • Edge: Same as Chrome (Chromium-based)

These differences mean that the same physical device produces different quota values in different browsers, adding another dimension to the fingerprint.

Common Protection Approaches and Their Limitations

VPNs and Proxy Servers

VPNs have no effect on storage quota values. The quota is determined entirely by the local disk configuration and browser calculation. Network-level privacy tools cannot modify local storage characteristics.

Incognito and Private Browsing

Private browsing modes change storage quota behavior, but not in a way that helps privacy. In incognito mode, some browsers report a significantly reduced quota (since data is stored in a temporary filesystem), and the quota may differ from normal mode. This difference itself becomes a signal for detecting incognito mode, rather than improving privacy.

Browser Extensions

Extensions can intercept the navigator.storage.estimate() promise and return modified values:

  • Fixed values: Returning a hardcoded quota (e.g., always 100 GB) is detectable if the value does not change across sessions or does not correlate with other disk-related signals.
  • Rounding: Rounding the quota to a common value reduces precision but may not match the expected value for the browser and OS combination being emulated.
  • Promise interception: Overriding navigator.storage.estimate or its prototype is detectable through property descriptor checks and prototype chain inspection.

Reducing Disk Usage

Using a smaller partition or disk does change the quota, but users cannot easily control their disk size for privacy purposes. This is a hardware-level characteristic that is fundamentally difficult to modify.

BotBrowser's Engine-Level Approach

BotBrowser controls storage quota responses at the browser engine level. When a fingerprint profile is loaded, all storage-related queries return values defined by the profile rather than calculated from the host machine's actual disk.

Profile-Based Quota Values

chrome --bot-profile="/path/to/profile.enc" \
       --user-data-dir="$(mktemp -d)"

The profile includes storage quota values captured from real devices. These values are realistic for the profile's target device class: a profile representing a laptop with a 512 GB SSD reports quota values consistent with that configuration, regardless of the host machine's actual disk size.

Internally Consistent Storage Signals

BotBrowser ensures that all storage-related APIs produce consistent results:

  • navigator.storage.estimate() returns profile-defined quota and usage values
  • IndexedDB storage behavior aligns with the reported quota
  • Cache API behavior is consistent with the storage budget
  • The quota/usage ratio is realistic for the target device class
  • Values remain stable throughout the session

Cross-Platform Accuracy

Because different browsers calculate quotas differently, BotBrowser's profiles include browser-specific quota values. A Chrome profile returns Chrome-appropriate quota calculations, while the underlying formula differences between browsers are accounted for in the profile data.

Preventing Host Disk Leaks

The primary goal is to prevent the host machine's disk configuration from leaking through storage APIs. Whether BotBrowser runs on a 128 GB server or a 4 TB workstation, the storage quota values reported to web pages reflect the profile's target device, not the actual host hardware.

Usage Value Control

The usage value (current storage consumed) is also controlled. A freshly loaded profile reports minimal usage, and the value evolves naturally as the origin stores data during the session, but always within the context of the profile's quota framework.

Configuration and Usage

Basic CLI Usage

Storage quota protection is automatic when loading a profile:

chrome --bot-profile="/path/to/profile.enc" \
       --user-data-dir="$(mktemp -d)"

No additional flags are needed.

Playwright Integration

const { chromium } = require('playwright-core');

(async () => {
  const browser = await chromium.launch({
    executablePath: '/path/to/botbrowser/chrome',
    args: [
      '--bot-profile=/path/to/profile.enc',
    ],
    headless: true,
  });

  const context = await browser.newContext({ viewport: null });
  const page = await context.newPage();

  const storageInfo = await page.evaluate(async () => {
    const estimate = await navigator.storage.estimate();
    return {
      quota: estimate.quota,
      usage: estimate.usage,
      quotaGB: (estimate.quota / (1024 ** 3)).toFixed(2),
      usageMB: (estimate.usage / (1024 ** 2)).toFixed(2),
    };
  });

  console.log('Storage info:', storageInfo);
  await browser.close();
})();

Puppeteer Integration

const puppeteer = require('puppeteer-core');

(async () => {
  const browser = await puppeteer.launch({
    executablePath: '/path/to/botbrowser/chrome',
    args: [
      '--bot-profile=/path/to/profile.enc',
    ],
    headless: true,
    defaultViewport: null,
  });

  const page = await browser.newPage();
  await page.goto('about:blank');

  const quota = await page.evaluate(async () => {
    const est = await navigator.storage.estimate();
    return { quota: est.quota, usage: est.usage };
  });

  console.log('Quota:', quota.quota, 'bytes');
  console.log('Usage:', quota.usage, 'bytes');
  await browser.close();
})();

Combined with Other Protections

For comprehensive device identity control:

chrome --bot-profile="/path/to/profile.enc" \
       --bot-noise-seed=42 \
       --proxy-server="socks5://user:pass@proxy:1080" \
       --bot-config-timezone="America/Chicago" \
       --user-data-dir="$(mktemp -d)"

Verification

After launching BotBrowser with a profile, verify storage quota values:

const estimate = await navigator.storage.estimate();
const quotaGB = (estimate.quota / (1024 ** 3)).toFixed(2);
const usageMB = (estimate.usage / (1024 ** 2)).toFixed(2);

console.log(`Quota: ${quotaGB} GB (${estimate.quota} bytes)`);
console.log(`Usage: ${usageMB} MB (${estimate.usage} bytes)`);

// Check that quota reflects the profile, not the host
// A profile for a 256 GB device should show roughly 60% of 256 GB
// A profile for a 1 TB device should show a proportionally larger value

What to check:

  1. Quota value is realistic for the profile's target device class (not the host machine)
  2. Quota does not match your actual host disk size
  3. Usage value is reasonable (low for a fresh session, increasing as data is stored)
  4. Values remain stable across page reloads within the same session
  5. Different profiles produce different quota values
  6. Fingerprint testing tools show no anomalies related to storage

Best Practices

  1. Use complete profiles. Storage quota values must be consistent with the rest of the device identity. A profile claiming to be a mobile device should not report a 2 TB quota.

  2. Match user-data-dir to the use case. A fresh user-data-dir (using mktemp -d) starts with minimal usage. For persistent sessions, reuse the same data directory so that usage values evolve naturally.

  3. Consider incognito detection. If your workflow requires avoiding incognito detection, be aware that storage quota behavior differs between normal and incognito mode. BotBrowser profiles in normal mode report standard quota values.

  4. Verify against your actual host. Run navigator.storage.estimate() on your host machine outside of BotBrowser to know your real quota value, then confirm that BotBrowser's profile reports a different value.

Frequently Asked Questions

Does storage quota reveal the exact disk size?

Not exactly. The quota is a percentage of available disk space (roughly 60% on Chrome), and it changes as disk space is used. However, the quota value reliably distinguishes between disk size tiers (128 GB vs. 256 GB vs. 512 GB vs. 1 TB), making it a useful fingerprinting signal.

Can websites probe storage limits without navigator.storage.estimate()?

Yes. By attempting to write increasingly large blobs to IndexedDB or the Cache API, a website can discover the storage limit through trial and error. BotBrowser controls the underlying quota system, so both the estimate API and actual storage behavior reflect the profile's configuration.

Does storage quota change in incognito mode?

Yes. Incognito mode typically uses a temporary filesystem with a different quota. The quota value in incognito is often significantly lower than in normal mode, which can be used to detect incognito browsing. BotBrowser's normal-mode profiles report standard quota values.

How precise is the quota as a fingerprint?

The quota value is returned in bytes, providing very high precision. However, because the value fluctuates as disk space changes, it is most useful as a coarse identifier (disk size tier) rather than a precise one. When combined with other signals, it contributes meaningfully to the overall fingerprint.

Does BotBrowser control the Persistent Storage API?

The navigator.storage.persist() method and its associated behavior are controlled by the profile. The profile determines whether persistent storage is available and how quota calculations are affected by persistence status.

What about the deprecated webkitStorageInfo API?

Older Chromium versions exposed storage quota through webkitStorageInfo and navigator.webkitTemporaryStorage. BotBrowser profiles control these legacy APIs as well, ensuring that older fingerprinting scripts receive consistent values.

Can storage quota reveal if I am running in a container?

Containers and virtual machines often have smaller disk allocations than physical machines. A Docker container with a 20 GB volume produces a very different quota than a physical machine with a 1 TB SSD. BotBrowser's profile-based approach ensures that the reported quota matches the target device, not the container's actual storage.

Summary

The Storage Manager API's navigator.storage.estimate() exposes disk characteristics that serve as a persistent fingerprinting signal. The quota value indirectly reveals disk size, device class, and operating system through browser-specific calculation formulas. BotBrowser controls all storage quota responses at the engine level through its profile system, ensuring that reported values match the target device and do not leak information about the host machine's actual disk configuration. For related protection, see navigator properties protection, performance timing control, and comprehensive profile management.

#storage#quota#fingerprinting#privacy#indexeddb#disk