Documentation Blog Free tools [email protected]Log in

How to browser fingerprint: signals, code and countermeasures

Browser signals are collected, hashed into one identifier and compared against a stored fingerprint1. Collect weak signalsUser-Agent + client hintsScreen, timezone, localeCanvas + WebGL renderAudio stack, fontsTLS ClientHello (JA4)hash()a3f9-71c2-04beentropy scoredMatch stored IDknown device: allowMismatch or impossiblechallenge, MFA or 403No cookie is stored — the same environment regenerates the same ID after a cache clear

To browser fingerprint a visitor you collect dozens of low-entropy signals in one page load — headers, client hints, screen and timezone, canvas and WebGL render output, audio stack, font metrics, plus the TLS ClientHello — normalise them, hash them into one identifier, and store that hash server-side to recognise the same environment on later visits without any cookie.

What a browser fingerprint actually is

A device fingerprint is information collected about the software and hardware of a remote machine for the purpose of identification, assimilated into a short identifier by a fingerprinting algorithm; a browser fingerprint is the subset gathered specifically through the browser (Wikipedia, Device fingerprint). The important property is that nothing is written to the client. Clear cookies, open incognito, switch to a VPN — the same hardware and software configuration regenerates approximately the same hash.

Two properties decide whether a fingerprint is useful: diversity (does it separate you from millions of others?) and stability (does it survive a browser update?). They trade off. Adding another setting to the hash raises entropy but breaks the identifier the moment a user changes that setting. The EFF's Panopticlick study measured at least 18.1 bits of entropy from browser fingerprinting alone, with canvas claiming roughly 5.7 more; later work found 83.6% to 89.4% of desktop fingerprints unique, while the 2018 "Hiding in the Crowd" dataset put uniqueness at 33.6% overall and only 18.5% on mobile (figures collected in the same Wikipedia article). Mobile devices are simply less varied.

That gap explains why production systems do not hash-and-compare naively. They weight each signal by uniqueness and durability, then link partially-different fingerprints with rule-based or machine-learned matching — for example treating two fingerprints identical except for a monotonically increasing browser version as the same device.

How to browser fingerprint, step by step

  1. Read the transport layer first. Before any JavaScript runs, the TLS ClientHello exposes version list, cipher-suite ordering, supported groups and extensions. JA3 (MD5 over those fields) and the newer JA4 (stable under extension shuffling, with HTTP and QUIC context) turn that into a string. A plain Python requests handshake is identifiable here no matter how perfectly you spoof headers.
  2. Capture headers and client hints. User-Agent, Accept-Language, Accept-Encoding order, HTTP/2 pseudo-header order and settings frames, and the Sec-CH-UA-* hint family. Cross-check them: a UA claiming macOS with Sec-CH-UA-Platform: "Windows" is a hard fail.
  3. Query the JS environment. navigator.platform, hardwareConcurrency, deviceMemory, maxTouchPoints, languages, screen.width/height/colorDepth, Intl.DateTimeFormat().resolvedOptions().timeZone, permission states, media-device counts.
  4. Force rendering work. Draw text and shapes to an offscreen HTML5 canvas and hash the pixel buffer; read WEBGL_debug_renderer_info for the GPU and driver string; run an OfflineAudioContext oscillator and hash the output buffer. These reveal GPU, drivers and CPU-level float behaviour.
  5. Measure fonts by side channel. Render a string in a candidate font and compare its measured width against a fallback. Font lists are highly identifying — one cited study found 34% of a population identifiable from 43 characters of font data.
  6. Normalise, weight, hash. Sort keys, round noisy floats, drop signals you know to be unstable, then hash. Store the hash plus the raw component vector so you can re-link when one component drifts.
  7. Score, do not just match. Combine the fingerprint with IP reputation, ASN, and behavioural timing into a risk number. Fingerprint alone answers "same environment?", not "human?".

A minimal browser fingerprint generator

// Runs client-side; post the result to your own endpoint.
async function fingerprint() {
  const c = document.createElement('canvas');
  const ctx = c.getContext('2d');
  ctx.textBaseline = 'alphabetic';
  ctx.font = '16px "Arial"';
  ctx.fillStyle = '#f60';
  ctx.fillRect(0, 0, 100, 30);
  ctx.fillStyle = '#069';
  ctx.fillText('Cwm fjord veg 12345', 2, 20);

  const gl = document.createElement('canvas').getContext('webgl');
  const dbg = gl && gl.getExtension('WEBGL_debug_renderer_info');

  const signals = {
    ua: navigator.userAgent,
    langs: navigator.languages.join(','),
    tz: Intl.DateTimeFormat().resolvedOptions().timeZone,
    screen: [screen.width, screen.height, screen.colorDepth].join('x'),
    cores: navigator.hardwareConcurrency || 0,
    mem: navigator.deviceMemory || 0,
    touch: navigator.maxTouchPoints,
    canvas: c.toDataURL(),
    gpu: dbg ? gl.getParameter(dbg.UNMASKED_RENDERER_WEBGL) : 'none',
    webdriver: navigator.webdriver === true
  };

  const bytes = new TextEncoder().encode(JSON.stringify(signals));
  const digest = await crypto.subtle.digest('SHA-256', bytes);
  return [...new Uint8Array(digest)]
    .map(b => b.toString(16).padStart(2, '0')).join('').slice(0, 32);
}

That is a working fingerprint in forty lines, and it is also why open-source projects on GitHub are the usual starting point for people searching "browser fingerprinting github" — the collection logic is not secret. The hard part is the server side: entropy weighting, drift handling and linkage.

Fingerprinting methods compared

MethodWhat it revealsEntropyStabilityDefeated by
Headers + client hintsBrowser, OS, localeLowHighTrivial spoofing (but easy to cross-check)
CanvasGPU, drivers, font rasterisationHighHighPer-session noise injection
WebGLRenderer and vendor strings, capabilitiesHighHighExtension blocking, software rendering
Audio (Web Audio API)CPU/DSP float behaviourMediumHighValue randomisation
Font enumerationInstalled font setHighMediumDefault OS font list only
Media devicesCameras, mics, speakers attachedMediumMediumPermission denial
TLS (JA3/JA4)The HTTP client itselfMediumVery highOnly a real browser stack
BehaviouralMouse jitter, scroll and click timingMediumLow per sessionRealistic input synthesis

Why automation gets caught

Fingerprinting was built for fraud teams — detecting account takeover from an unrecognised device, linking accounts controlled by one operator, spotting emulator farms — and it works equally well against scrapers. A default headless Chromium leaks in predictable ways: navigator.webdriver === true, a HeadlessChrome token, an empty plugin array, a SwiftShader WebGL renderer where the UA claims an NVIDIA desktop, a screen size that matches no shipping display, and a permissions object that answers differently from real Chrome.

Detection rarely relies on a single tell. It relies on contradiction. A residential IP in Lisbon paired with an America/New_York timezone, French language headers and a JA4 hash that belongs to a Go HTTP client is not one problem — it is four mutually inconsistent claims, and inconsistency is the cheapest bot signal there is. The corollary for anyone building automation: internal coherence matters more than any individual value you patch.

Consistent versus contradictory signal sets and their outcomesCoherent environmentIP Lisbon · TZ Europe/Lisbon · pt-PTJA4 = Chrome 131 · GPU matches UALow risk scorepage served, no challengeContradictory environmentIP Lisbon · TZ America/New_YorkJA4 = Go client · webdriver trueChallenge or 403interstitial, CAPTCHA, block

How to avoid browser fingerprinting

There are two distinct goals here and they need different answers.

As a privacy-minded user

Reduce entropy rather than fake it. Firefox's fingerprinting protection (part of Enhanced Tracking Protection, on by default for all browsing since Firefox 69) blocks known fingerprinting scripts and, in resist-fingerprinting mode, normalises values so you look like everyone else running the same build. Tor Browser takes that furthest. Keep default settings, avoid exotic font and extension combinations — research cited by Wikipedia found 56.86% of users have a unique extension set — and remember that a browser fingerprint protector extension can backfire: an environment that reports impossible values is more distinctive than a boring one, and vendors treat anti-fingerprinting plugins as a risk flag in their own right.

As an engineer running automation

Patching individual leaks is a treadmill. The stack you need looks like this, and each layer has to agree with the others:

  • Transport: a real browser TLS/HTTP2 stack, or a client that reproduces one faithfully. No amount of header work fixes a wrong JA4.
  • Network: exit IPs whose type, geography and ASN match the story your headers tell. Residential proxies from $0.80/GB with per-request country targeting keep IP geo and locale aligned; ISP proxies give you static addresses when a session needs to look like the same returning household.
  • Browser environment: coherent canvas, WebGL, audio and font profiles that belong to a device that actually exists.
  • Behaviour: variable dwell time and non-linear input, because behavioural signals feed the same risk score.

Or move the problem to an API

Most teams do not want to maintain a fingerprint farm; they want the page content. A web scraping API keeps the fingerprint surface — TLS profile, browser environment, proxy selection, retries — behind one HTTP call and returns Markdown, HTML or structured JSON. On QuanticData that starts at $0.0002 per page, or $0.001 with JS rendering, and pay-per-success means a blocked attempt costs nothing:

curl https://api.quanticdata.io/v1/scrape \
  -H "Authorization: Bearer $QD_API_KEY" \
  -d '{ "url": "https://example.com/pricing", "render": true }'

{ "success": true,
  "data": { "markdown": "# Pricing\n…" },
  "usage": { "cost_usd": 0.001 },
  "retries": [ { "attempt": 1, "outcome": "blocked" },
               { "attempt": 2, "outcome": "ok" } ] }

Honest cost maths

Compare that to building it. One engineer maintaining fingerprint profiles, TLS emulation and block triage is not a part-time job; call it a conservative fraction of a salary plus browser compute and proxy bandwidth. At $0.001 per rendered page you can fetch a million pages for $1,000 — and you only pay for the ones that came back. Self-hosting wins when you need custom in-page interaction or you are already running browser infrastructure; the API wins for read-heavy extraction, which is most scraping.

For agent workflows the same logic applies through MCP: a model calls search, scrape, map or crawl as tools and never sees a fingerprint problem, because the tool boundary is where that complexity lives. The web data API for AI returns the same envelope to every tool call, so a failed fetch is a typed error your agent can retry rather than an HTML challenge page it tries to summarise. For whole-site jobs, crawl and map handle pagination and URL discovery at $0.0003 per page with unfetched pages refunded.

Legality and ethics

Fingerprinting for security purposes is generally treated as lawful. Vendors and privacy regulators broadly agree that using it to detect fraud, protect accounts and block bots can rest on legitimate interest under GDPR, while tracking and personalisation attract disclosure and consent duties; CCPA imposes its own notice and opt-out obligations. MDN documents fingerprinting as a tracking technique browsers actively work to frustrate, which is a useful signal about where platform policy is heading.

Evading fingerprinting sits in a different bucket. Testing your own systems, building reliable first-party automation and collecting public data within a site's terms and applicable law are ordinary engineering. Defeating fingerprinting to commit fraud, bypass authentication or scrape personal data is not. We cover the boundaries in more depth in is browser fingerprinting legal and is device fingerprinting legal. None of this is legal advice — check your jurisdiction and the terms of the sites you touch.

A short checklist

  1. Decide whether you need identification (fingerprint) or bot classification (risk score). They are not the same product.
  2. Collect at the transport layer as well as in JavaScript, or you will miss the most stable signal you have.
  3. Weight signals by entropy and durability; store components, not just the hash, so you can re-link after drift.
  4. If you are on the automation side, test coherence, not individual values — one contradiction defeats a hundred patches.
  5. Price the build against pay-per-success extraction before you commit an engineer to it.

Sources & further reading

FAQ

Quick answers on how to browser fingerprint.

Something else? Ask us →

How do you create a browser fingerprint?

Collect signals in one page load — headers and client hints, screen and timezone, canvas and WebGL output, audio buffer, font metrics, hardware counts — normalise them, then hash the combined vector into a short identifier stored server-side. Add the TLS ClientHello fingerprint (JA3/JA4) from the connection itself for a signal JavaScript cannot fake.

How do I avoid browser fingerprinting?

Reduce entropy instead of faking values: keep default settings, limit extensions and custom fonts, and use a browser with built-in protection such as Firefox's fingerprinting protection or Tor Browser. Aggressive spoofing via a browser fingerprint protector extension often makes you more distinctive and triggers CAPTCHAs, because impossible value combinations read as risk.

Is browser fingerprinting legal?

Generally yes. Under GDPR, using it for security, fraud detection and bot prevention is typically justified as legitimate interest, while tracking and personalisation carry disclosure and consent obligations; CCPA adds notice and opt-out duties in California. Rules vary by jurisdiction and use case, so treat this as background, not legal advice.

Can browser fingerprinting be cleared like cookies?

No. A fingerprint is stateless — nothing is written to your device, so the hash lives in a server-side database beyond your reach. Clearing cookies, using incognito or switching browsers does not reset it, because the same hardware and software configuration regenerates a very similar identifier on the next visit.

What is the difference between browser and device fingerprinting?

Browser fingerprinting uses attributes exposed by one browser: headers, JS APIs, canvas, WebGL, fonts. Device fingerprinting aims at the machine itself — OS, CPU, GPU, network attributes and mobile SDK identifiers — so it can recognise the same device across different browsers and apps, and tends to stay stable for longer.

Why does my scraper get blocked even with good proxies?

Because proxies only fix the IP. Anti-bot systems also read your TLS fingerprint, headless markers such as navigator.webdriver, WebGL renderer strings and behavioural timing. A residential IP paired with a Python TLS handshake and a headless browser profile is a contradiction, and contradictions are the cheapest bot signal available.

Skip the fingerprint treadmill

QuanticData handles TLS profiles, browser environments and proxy selection behind one API call — any page as clean Markdown, HTML or JSON from $0.0002 ($0.001 rendered), pay per success so blocked attempts cost nothing. Start with $2 of free usage every month, no card required.

Related reading