Browser Fingerprinting & User Identification

Identifying users without cookies — how fingerprinting works and its ethical implications

The User Identification Challenge

A fundamental analytics challenge is identifying unique users across visits. Cookies are the traditional mechanism — set a unique ID in a cookie, and you recognize the user when they return. But cookies can be cleared, blocked by browsers, rejected by privacy-conscious users, or simply not persist in incognito mode. Fingerprinting is the alternative.

What Is Browser Fingerprinting?

Browser fingerprinting combines multiple browser and device attributes to create a quasi-unique identifier. No single attribute is unique — millions of people use Chrome on Windows — but the combination of attributes is surprisingly distinctive. It works without cookies, without login, and without any stored state on the client.

Attribute Source How It Helps
User-Agent string HTTP header Browser + OS + version
Screen resolution screen.width/height Device type
Installed fonts Canvas/JS enumeration Highly variable across systems
Canvas rendering Canvas API GPU/driver-specific pixel differences
WebGL renderer WebGL API GPU hardware identifier
Timezone Intl.DateTimeFormat Geographic signal
Language navigator.language Locale preference
Platform navigator.platform OS identifier
Hardware concurrency navigator.hardwareConcurrency CPU core count
Audio fingerprint AudioContext API Audio stack differences
Installed plugins navigator.plugins (legacy) Increasingly limited

How Fingerprint Hashing Works

The process is conceptually simple: collect attributes, concatenate them into a string, and hash the result to produce a fingerprint ID:

// Conceptual fingerprint generation async function generateFingerprint() { const components = [ navigator.userAgent, screen.width + 'x' + screen.height, navigator.language, navigator.platform, navigator.hardwareConcurrency, Intl.DateTimeFormat().resolvedOptions().timeZone, getCanvasFingerprint(), // render text to canvas, extract pixel data getWebGLRenderer() // query GPU info via WebGL ]; const raw = components.join('|'); const hash = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(raw)); return Array.from(new Uint8Array(hash)) .map(b => b.toString(16).padStart(2, '0')).join(''); } // Result: something like "a3f2b8c1e9d4..." — fairly stable across visits

The resulting hash is fairly stable across visits from the same browser on the same device. It changes when the user updates their browser, installs new fonts, or changes OS settings — but it persists through cookie clearing and incognito mode.

Effectiveness and Limitations

Studies have shown that canvas rendering + WebGL + fonts can uniquely identify over 90% of desktop browsers. However, fingerprinting is not perfect:

  • Mobile devices are less unique — Fewer customizations, similar hardware across millions of iPhone users
  • Browser updates change the fingerprint — A Chrome update changes the User-Agent string, shifting the hash
  • Incognito mode does NOT defeat fingerprinting — The hardware, fonts, and GPU are still the same
  • It is probabilistic, not exact — Two users with identical hardware, OS, and settings will share a fingerprint

Fingerprinting vs. Cookies

Aspect Cookies Fingerprinting
User can clear Yes No
Blocked by ad blockers Sometimes Harder to block
Stability Until cleared Until browser/OS updates
Accuracy Exact (unique ID) Probabilistic (~90%+)
Privacy regulation Covered by GDPR Also covered by GDPR
Cross-device No (unless synced) No (different hardware)

Privacy and Ethics

GDPR considers fingerprinting personal data — consent is required just as it is for cookies. But unlike cookies, users cannot easily see, inspect, or clear a fingerprint. This asymmetry is ethically problematic. Browsers are actively fighting fingerprinting: Firefox offers "fingerprinting protection" that standardizes canvas output and hides hardware details, Safari's Intelligent Tracking Prevention (ITP) limits fingerprintable APIs, and Chrome has signaled similar intentions.