Skip to content

Browser Fingerprinting: How Websites Track You

💡
Before you start

Python 3 and a terminal. Nothing is installed, nothing is changed on your machine, and no website is contacted — the whole lab is arithmetic you run locally.

You need no maths background. The one idea is that bits measure how much a detail narrows a crowd: each bit halves it. Ten bits pick one person in about a thousand.

If you have never opened a terminal, that is fine. Every command is typed exactly as written, and step 1 says which terminal to open.

What Is Browser Fingerprinting?

Browser fingerprinting is a tracking technique that identifies you based on your browser and device configuration rather than storing data on your computer. Unlike cookies, which you can delete, a fingerprint is assembled from information your browser willingly shares with every website you visit.

When combined, these seemingly harmless details create a unique identifier that can track you across websites without your knowledge or consent. Research shows that over 90% of browsers have a unique fingerprint, making this one of the most effective tracking methods in use today.

💡
Cookies vs. Fingerprinting

Cookies store a tracking ID on your device (you can delete them). Fingerprinting reads your device's unique characteristics (you can't "delete" your screen resolution or installed fonts). This is why fingerprinting is often called "cookieless tracking."

What Data Makes Up a Fingerprint?

Websites collect dozens of data points from your browser. Each one alone is not unique, but together they form a highly distinctive profile:

  • User-Agent string — Your browser name, version, and operating system
  • Screen resolution — Display dimensions and color depth
  • Installed fonts — The list of fonts available on your system
  • Timezone — Your local time offset from UTC
  • Language settings — Browser language and preferred languages list
  • Installed plugins — Active browser plugins and their versions
  • Canvas rendering — How your GPU draws a hidden image (varies by hardware)
  • WebGL data — Graphics card model, driver, and rendering capabilities
  • AudioContext — How your device processes audio signals
  • Hardware concurrency — Number of CPU cores
  • Device memory — Amount of RAM reported by the browser
  • Touch support — Whether your device has a touchscreen
  • Do Not Track setting — Ironically, enabling DNT makes you more unique

Canvas Fingerprinting

Canvas fingerprinting is one of the most powerful techniques. A website draws an invisible image using the HTML5 Canvas API and reads the pixel data back. Because different GPUs, drivers, and operating systems render the image slightly differently, the result is a nearly unique hash.

This works because graphics rendering is affected by:

  • GPU manufacturer and model
  • Graphics driver version
  • Operating system font rendering engine
  • Sub-pixel rendering and anti-aliasing settings
⚠️
Hard to block completely

Blocking canvas access entirely can break legitimate websites. The best approach is to add noise to the canvas output rather than blocking it outright.

WebGL and AudioContext Fingerprinting

WebGL fingerprinting queries your graphics card's renderer and vendor strings, plus supported extensions. A website can render a complex 3D scene and hash the output, similar to canvas fingerprinting but using the WebGL API.

AudioContext fingerprinting exploits differences in how devices process audio. A website generates a sound wave using the Web Audio API and measures the output. Variations in audio hardware and drivers produce different results across devices.

Testing Your Browser Fingerprint

Before making changes, check how unique your current fingerprint is:

1
Cover Your Tracks (EFF)

Visit coveryourtracks.eff.org — the Electronic Frontier Foundation's tool tests your browser against real-world fingerprinting techniques and shows exactly which attributes make you unique.

2
CreepJS

Visit abrahamjuliot.github.io/creepjs — a more advanced fingerprinting test that checks canvas, WebGL, audio, fonts, and many other vectors. It also detects anti-fingerprinting tools.

3
Compare results

Run these tests before and after applying protections to measure the actual improvement. The goal is not to be invisible but to blend in with a large group.

Reducing Your Fingerprint

Complete anonymity through fingerprint resistance is extremely difficult. The practical goal is to make your fingerprint less unique by blending in with as many other users as possible.

Firefox: Enhanced Fingerprinting Protection

Firefox has built-in fingerprinting resistance. Open about:config and set:

privacy.resistFingerprinting = true

This single setting enables a wide range of protections:

  • Reports a generic user-agent and platform
  • Rounds screen resolution to standard sizes
  • Spoofs timezone to UTC
  • Limits font enumeration to a standard set
  • Adds noise to canvas and WebGL readouts
  • Returns fixed values for hardware concurrency and device memory
💡
Trade-offs

privacy.resistFingerprinting may break some websites. Dark mode is forced off, timezone shows as UTC (affecting calendar apps), and some canvas-dependent features may not work correctly. Disable it temporarily on sites that break.

For additional protection in Firefox, also consider:

webgl.disabled = true                    // Blocks WebGL fingerprinting (breaks some sites)
media.peerconnection.enabled = false     // Blocks WebRTC IP leaks
privacy.trackingprotection.enabled = true // Enables Enhanced Tracking Protection

Anti-Fingerprinting Browser Extensions

Several extensions can help reduce fingerprinting:

  • CanvasBlocker — Adds random noise to canvas, WebGL, and AudioContext readouts instead of blocking them. This is better than outright blocking because it doesn't break websites while still defeating fingerprinting.
  • uBlock Origin — Blocks known fingerprinting scripts from loading in the first place. Enable the "EasyPrivacy" and "Peter Lowe's Ad and tracking server list" filter lists.
  • Privacy Badger — Learns and blocks invisible trackers, including some fingerprinting scripts, based on their behavior across multiple sites.
⚠️
Avoid too many extensions

Each extension you install can paradoxically increase your fingerprint uniqueness. The combination of extensions itself becomes a data point. Stick to 2-3 well-chosen extensions.

Tor Browser: The Gold Standard

The Tor Browser is designed from the ground up to resist fingerprinting. All Tor Browser users share the same fingerprint because:

  • Same user-agent string for all users
  • Same window size (rounded to multiples of 200x100)
  • Same timezone (UTC)
  • Same font list (bundled fonts only)
  • Canvas and WebGL return uniform results
  • JavaScript timing precision is reduced

If fingerprinting resistance is your top priority, Tor Browser is the most effective solution. See our Complete Tor Browser Guide for setup instructions.

Brave Browser Approach

Brave takes a different approach to fingerprinting than Firefox. Instead of trying to make all users look identical, Brave randomizes fingerprint values on every session:

  • Canvas and WebGL outputs are randomized per-session and per-domain
  • Hardware concurrency and device memory are spoofed
  • Language preferences are limited
  • Randomization happens automatically with no configuration needed

This means every time you visit a site, your fingerprint looks different, making it impossible to track you across sessions. The advantage over Tor's approach is that fewer websites break.

What Fingerprinting Cannot Track

Understanding the limitations of fingerprinting helps you make informed privacy decisions:

  • Different browsers on the same device — Firefox and Chrome produce completely different fingerprints
  • After a major OS update — New drivers, new fonts, or browser updates change the fingerprint
  • Virtual machines — VMs have generic hardware profiles that blend in
  • Private/Incognito mode — Does NOT protect against fingerprinting (a common misconception)
⚠️
Incognito mode does NOT prevent fingerprinting

Private browsing only prevents local data storage (history, cookies). Your browser still exposes the same fingerprinting data points to websites. You need the techniques described above for actual fingerprint protection.

Practical Protection Strategy

Here is a layered approach, from least effort to maximum protection:

1
Basic (everyone should do this)

Use Firefox with Enhanced Tracking Protection set to "Strict". Install uBlock Origin. This blocks known fingerprinting scripts without changing your browsing experience.

2
Moderate (privacy-conscious users)

Enable privacy.resistFingerprinting in Firefox. Add CanvasBlocker extension. Accept minor website breakage for significantly reduced trackability.

3
Maximum (when anonymity matters)

Use Tor Browser for sensitive browsing. Keep a separate Firefox profile for everyday use. Never log into personal accounts from Tor (it defeats the purpose).

Now Do It Yourself: Watch Six Ordinary Details Identify One Person

The claim that over 90% of browsers are unique is easy to read past. It is also arithmetic you can run in five minutes, and doing so changes which defences look sensible — including one that is widely recommended and makes things worse.

You need only Python, which is already on macOS and Linux and installs from python.org on Windows. Every output below came from running these exact scripts. The percentages are realistic round numbers, not measurements of any real population — the point is the shape of the arithmetic, which does not depend on the exact figures.

1
Build the funnel and watch a crowd become one person

Go: open a terminal and make a folder to work in: mkdir fplab then cd fplab.

Do: save this as fingerprint.py. It starts with 100,000 people and applies one attribute at a time.

import math

POPULATION = 100_000

# Roughly what fraction of people share your value for each attribute.
ATTRS = {
    "browser + version": 0.08,
    "operating system":  0.30,
    "screen resolution": 0.20,
    "timezone":          0.05,
    "language":          0.40,
    "installed fonts":   0.01,
}

print(f"Starting population: {POPULATION:,} people\n")
remaining = float(POPULATION)
bits = 0.0
for name, share in ATTRS.items():
    remaining *= share
    bits += -math.log2(share)
    print(f"  + {name:<18} -> {remaining:12,.1f} people still match   ({bits:5.1f} bits known)")

print(f"\nAfter {len(ATTRS)} attributes: {remaining:,.2f} people match you.")
print(f"That is {bits:.1f} bits of identifying information.")
print(f"{bits:.1f} bits distinguishes 1 person in {2**bits:,.0f}.")

Then run it:

python3 fingerprint.py

You should see: the crowd collapsing. Six ordinary details are enough:

Starting population: 100,000 people

  + browser + version  ->      8,000.0 people still match   (  3.6 bits known)
  + operating system   ->      2,400.0 people still match   (  5.4 bits known)
  + screen resolution  ->        480.0 people still match   (  7.7 bits known)
  + timezone           ->         24.0 people still match   ( 12.0 bits known)
  + language           ->          9.6 people still match   ( 13.3 bits known)
  + installed fonts    ->          0.1 people still match   ( 20.0 bits known)

After 6 attributes: 0.10 people match you.
That is 20.0 bits of identifying information.
20.0 bits distinguishes 1 person in 1,041,667.

Not one of those six is secret. Your timezone is not private; neither is your screen size. The identification is in the combination, and that is the part a cookie banner cannot help with.

If not: python3: command not found on Windows usually means Python is installed as py — use py fingerprint.py. If you get SyntaxError near an f-string, your Python is older than 3.6; install a current version from python.org.

2
Find out which single attribute is doing the work

Go: the same file.

Do: fonts are the most identifying line at 1%. Make them ordinary instead — change that one number from 0.01 to 0.50, so half of everyone shares your font list. Save, and run it again.

python3 fingerprint.py

You should see: the last line changes dramatically. The final count rises from 0.10 people to about 4.8, and the bits fall from 20.0 to about 14.3 — roughly 1 in 20,000 instead of 1 in a million.

One attribute moved the answer by a factor of fifty. That is why font enumeration and canvas rendering get so much attention: rare details are worth far more to a tracker than common ones, and a single unusual thing about you can undo everything ordinary.

If not: if nothing changed, you edited a different line or did not save. The line to change is the one ending "installed fonts": 0.01, — edit only the number.

3
Read your own values, with no website involved

Go: the same terminal.

Do: two of those six attributes are visible from your own machine. Ask it:

date +%Z
date +%z
echo $LANG

You should see: your timezone and language — the same values every website you visit is handed automatically:

EEST
+0300
en_US.UTF-8

Yours will differ. On Windows PowerShell use Get-TimeZone and $env:LANG.

Nobody asked your permission for these, and no setting turns them off — a website needs your timezone to show times correctly and your language to pick a translation. Fingerprinting is built out of information that is genuinely needed, which is why blocking it outright is not an option.

If not: an empty line after echo $LANG means the variable is unset, which is normal on some systems — try locale instead. On macOS date +%Z works the same as on Linux.

4
The trap: make yourself unusual and see it backfire

Go: the same folder.

Do: save this second script as blending.py. It compares three browsers: ordinary, "hardened" with a rare extension, and one where everyone looks the same.

import math

def bits(shares):
    return sum(-math.log2(s) for s in shares)

base = [0.08, 0.30, 0.20, 0.05, 0.40, 0.01]
print("A. Ordinary browser, default settings")
print(f"   {bits(base):.1f} bits  ->  1 in {2**bits(base):,.0f}\n")

hardened = [0.001, 0.30, 0.20, 0.05, 0.40, 0.01, 0.002]
print("B. Same browser + a rare 'privacy' extension that makes you unusual")
print(f"   {bits(hardened):.1f} bits  ->  1 in {2**bits(hardened):,.0f}")
print("   You did not hide. You became easier to pick out.\n")

uniform = [0.99, 0.99, 0.99, 0.99, 0.99, 0.99]
print("C. Tor Browser: everyone reports identical values")
print(f"   {bits(uniform):.1f} bits  ->  1 in {2**bits(uniform):,.1f}")
print("   Nothing to single you out, because you look like everyone else.")

Run it:

python3 blending.py

You should see: the middle case is by far the worst:

A. Ordinary browser, default settings
   20.0 bits  ->  1 in 1,041,667

B. Same browser + a rare 'privacy' extension that makes you unusual
   35.3 bits  ->  1 in 41,666,666,667
   You did not hide. You became easier to pick out.

C. Tor Browser: everyone reports identical values
   0.1 bits  ->  1 in 1.1
   Nothing to single you out, because you look like everyone else.

🔴 Read B again. Installing something that spoofs your user-agent to a value almost nobody uses made you forty thousand times easier to identify than doing nothing. The extension worked exactly as advertised — it changed what your browser reports — and the outcome was the opposite of privacy.

If not: if all three lines print the same number, you pasted the same list into all three variables. base, hardened and uniform must differ — check that hardened has seven numbers and starts with 0.001.

5
Prove what actually works

Go: blending.py.

Do: case C is the whole strategy behind Tor Browser: everyone reports the same thing. Test how fragile that is — change a single value in uniform from 0.99 to 0.05, as if you resized the window and became one of the few at that size. Save and run again.

python3 blending.py

You should see: case C jumps from 0.1 bits to about 4.4 bits — from 1 in 1.1 to roughly 1 in 21. Still far better than A or B, but one deviation cost most of the protection.

That is why Tor Browser opens at a fixed window size and asks you not to maximise it, and why it ships without the ability to install most extensions. Its defence is not hiding your details — it is making sure your details are identical to everyone else's. Any personalisation is a hole in that, which is the exact opposite of how privacy tools are usually sold.

If not: if C barely moves, you changed a number in base or hardened instead. The uniform list is the third one, all 0.99 values.

🎉
Check yourself before moving on

A friend says they are protected because they installed an extension that reports a fake, unusual user-agent. Using what step 4 showed, what is wrong with that? Answer: Rarity is what identifies you. A user-agent almost nobody else reports is a stronger signal than the real one — their fingerprint got more distinctive, not less. Protection comes from resembling many other people, not from being unusual.

Now do it without the page: work out, without running anything, why two people using stock Tor Browser on the same day are hard to tell apart, while two people who each hardened their own Firefox differently are easy. If you can explain that in one sentence, you understand fingerprinting better than most of the advice written about it.

Summary

Browser fingerprinting is a powerful tracking technique that works without cookies, making it harder to detect and block. While perfect anonymity is nearly impossible, you can significantly reduce your trackability:

  • Test your fingerprint to understand your starting point
  • Use Firefox with privacy.resistFingerprinting for daily browsing
  • Install CanvasBlocker and uBlock Origin (no more)
  • Use Tor Browser when anonymity is critical
  • Remember: the goal is to blend in, not to be invisible
🎉
Key Takeaway

Deleting cookies is not enough. Browser fingerprinting tracks you based on who your browser is, not what it stores. Combine fingerprint resistance with good extension hygiene for meaningful privacy improvements.