Privacy Risks of Browser Fingerprinting in 2026
Browser fingerprinting identifies users by combining dozens of browser and hardware attributes into a unique identifier β no cookies required. It survives private browsing, cookie clearing, and VPNs. This guide explains how each technique works, how to test your exposure, and which countermeasures are actually effective.
What Is Being Measured
A fingerprint is built from attributes that vary across user agents:
Hardware:
- Screen resolution and color depth
- GPU renderer string and vendor
- CPU core count
- Device memory (approximate)
- Battery status (deprecated, but some browsers still expose it)
Browser configuration:
- User agent string
- Installed plugins (mostly deprecated post-Flash)
- Accepted languages and timezone
- Do Not Track setting (ironic)
- Touch support and max touch points
Rendering:
- Canvas fingerprint (pixel-level rendering differences)
- WebGL renderer capabilities
- Font list inference from text rendering
- Audio fingerprint (AudioContext processing output)
Network:
- IP address
- HTTP Accept headers, header order
- TLS fingerprint (JA3)
Individually, these attributes are not unique. Combined, they create a fingerprint that identifies ~99.5% of browsers uniquely, according to the EFFβs Cover Your Tracks study.
Technique 1: Canvas Fingerprinting
The HTML5 Canvas API renders text and shapes. Rendering differences caused by GPU, OS fonts, anti-aliasing, and sub-pixel rendering produce pixel-level differences that are stable per device:
// How canvas fingerprinting works
function getCanvasFingerprint() {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
// Emoji and unusual characters force different rendering paths
ctx.textBaseline = 'top';
ctx.font = '14px Arial';
ctx.fillStyle = '#f60';
ctx.fillRect(125, 1, 62, 20);
ctx.fillStyle = '#069';
ctx.fillText('Canvas fingerprint π¦', 2, 15);
ctx.fillStyle = 'rgba(102, 204, 0, 0.7)';
ctx.fillText('Canvas fingerprint π¦', 4, 17);
// toDataURL() encodes the exact pixel values β same device = same hash
return canvas.toDataURL();
}
// Send to tracker backend
fetch('/fingerprint', {
method: 'POST',
body: JSON.stringify({ canvas: btoa(getCanvasFingerprint()) })
});
Resistance: Firefox randomizes canvas output (adds imperceptible noise per site). Brave blocks canvas by default. Chrome has no protection.
Technique 2: WebGL Fingerprinting
WebGL exposes GPU vendor, renderer string, and shader capabilities:
const gl = document.createElement('canvas').getContext('webgl');
const debugInfo = gl.getExtension('WEBGL_debug_renderer_info');
const vendor = gl.getParameter(debugInfo.UNMASKED_VENDOR_WEBGL);
const renderer = gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL);
// Example: "NVIDIA Corporation" / "GeForce RTX 4090/PCIe/SSE2"
// Combined with other attributes, this uniquely identifies most devices
Resistance: Brave blocks WEBGL_debug_renderer_info. Firefox (with privacy.resistFingerprinting) returns a generic string.
Technique 3: Audio Context Fingerprinting
The AudioContext API processes a generated tone. Hardware differences in the audio stack produce numerically different floating-point outputs:
function getAudioFingerprint() {
return new Promise(resolve => {
const context = new OfflineAudioContext(1, 44100, 44100);
const oscillator = context.createOscillator();
const compressor = context.createDynamicsCompressor();
oscillator.type = 'triangle';
oscillator.frequency.setValueAtTime(10000, context.currentTime);
oscillator.connect(compressor);
compressor.connect(context.destination);
oscillator.start(0);
context.startRendering().then(buffer => {
const data = buffer.getChannelData(0);
let fingerprint = 0;
for (let i = 0; i < data.length; i++) {
fingerprint += Math.abs(data[i]);
}
resolve(fingerprint.toString());
// Different hardware β different floating-point accumulation
});
});
}
Resistance: Firefox with privacy.resistFingerprinting returns a constant value. Brave adds deterministic noise.
Technique 4: Font Enumeration via CSS
Browsers can be probed for installed fonts by measuring text rendering width and height. If a font is not installed, the browser falls back to a default β a measurable difference:
function detectFont(fontName, testString = 'mmmmmmmmmmlli') {
const container = document.createElement('span');
container.style.cssText = 'position:absolute; left:-9999px; font-size:72px';
container.innerHTML = testString;
// Measure with fallback font
container.style.fontFamily = 'monospace';
document.body.appendChild(container);
const baseWidth = container.offsetWidth;
// Measure with target font
container.style.fontFamily = `'${fontName}', monospace`;
const fontWidth = container.offsetWidth;
document.body.removeChild(container);
return fontWidth !== baseWidth; // font is installed if dimensions differ
}
// Probe 400+ common fonts to build a list
const installedFonts = FONT_LIST.filter(detectFont);
Font lists are OS-specific and reveal whether youβre on Windows, macOS, or Linux β and often which OS version.
Technique 5: TLS Fingerprinting (JA3)
JA3 fingerprints the TLS handshake from the network layer β not the browser. Your TLS client hello message includes cipher suites, extensions, and elliptic curves in a specific order. This order is determined by your browser and OS, not by any setting you can change.
# Check your JA3 fingerprint
curl https://tls.browserleaks.com/json | python3 -m json.tool | grep ja3
# JA3 is used to:
# - Identify browser type and version (even with spoofed User-Agent)
# - Detect bots and headless browsers (Selenium, Playwright have different JA3)
# - Track users across Tor exit nodes and VPNs (same browser = same JA3)
Resistance: No browser allows changing the cipher suite order through UI. The only mitigation is using a browser that varies TLS parameters (Tor Browser does this by design) or using a TLS termination proxy.
Testing Your Fingerprint
# Open these in your current browser:
# EFF Cover Your Tracks
open https://coveryourtracks.eff.org
# Am I Unique (France) β large anonymity set database
open https://www.amiunique.org/fp
# BrowserLeaks β detailed breakdown of every API
open https://browserleaks.com
Browser Comparison
| Browser | Canvas | WebGL | Audio | Font | JA3 Resistance | Overall |
|---|---|---|---|---|---|---|
| Tor Browser | Uniform (Tor standard) | Blocked | Uniform | Uniform | Randomized | Strongest |
| Brave (Shields up) | Noise added | Blocked | Blocked | Limited probing blocked | Standard | Strong |
| Firefox + RFP | Uniform | Generic string | Uniform | Generic metrics | Standard | Strong |
| Safari | Some protection | Exposed | Exposed | Exposed | Standard | Medium |
| Chrome | Exposed | Exposed | Exposed | Exposed | Standard | Weak |
Firefox privacy.resistFingerprinting:
about:config β privacy.resistFingerprinting = true
This sets: uniform canvas, uniform timezone (UTC), uniform screen resolution (1000x900), generic language header. It breaks some sites.
Effective Countermeasures
| Technique | Countermeasure | Practical? |
|---|---|---|
| Canvas fingerprint | Firefox RFP or Brave noise injection | Yes |
| WebGL fingerprint | Brave (blocks debug info) | Yes |
| Audio fingerprint | Firefox RFP | Yes |
| Font enumeration | Firefox RFP returns uniform metrics | Yes |
| TLS fingerprint (JA3) | Tor Browser (randomized) | Yes, with trade-offs |
| IP address correlation | Tor or trusted VPN | Yes |
| User-agent uniformity | Tor Browser (all users same UA) | Yes |
The most effective single step: use Tor Browser for sensitive sessions and a hardened Firefox with privacy.resistFingerprinting for daily browsing.
Adding extensions (uBlock Origin, Privacy Badger) helps with first-party tracking but does not help with fingerprinting β the extension list itself becomes part of your fingerprint.
Related Reading
- Audit Chrome Extensions Privacy Guide
- Privacy Risks of Location Tracking Explained
- Anonymous Browsing on Mobile Devices Guide 2026
Built by theluckystrike β More at zovo.one