Privacy Tools Guide

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.



Built by theluckystrike β€” More at zovo.one