← Back to Blog
Browser window with code brackets, a data-point fingerprint and a privacy shield
Guides

How to Get a Browser Fingerprint in JavaScript: Code, Limits and Privacy Rules

Kenji Watanabe Kenji Watanabe Published on September 26, 2026 · inGuides

To get a browser fingerprint in JavaScript, read a set of properties the browser exposes (user agent, language, timezone, screen size, hardware hints), add a rendering test such as a canvas drawing, join the values into one string and hash it with the Web Crypto API. The result is a probabilistic identifier, not a stable ID, and in the EU collecting it usually requires consent.

This guide walks through that process in plain JavaScript, explains why the output changes more often than people expect, and sets out the privacy rules that apply. It is written for developers who want to understand what their own browser reveals, or who build fraud checks and security features. For the full catalogue of techniques, see our guide to browser fingerprinting techniques.

What a browser fingerprint is

The W3C's guidance on mitigating browser fingerprinting defines it as the capability of a site to identify or re-identify a visiting user, user agent or device through configuration settings or other observable characteristics. It separates two kinds:

  • Passive fingerprinting uses what arrives with every request, such as HTTP headers and the IP address, without running code on the device.
  • Active fingerprinting runs JavaScript or other code on the device to observe more characteristics, such as fonts, window size or device sensors.

Everything in this guide is active fingerprinting: the page asks the browser questions and records the answers. MDN's glossary entry lists typical ingredients: browser version, timezone and preferred language, available codecs, installed fonts, settings state, and display size and resolution.

Step 1: collect basic attributes

Most attributes come from standard objects and need no permission prompt:

function collectAttributes() {
  return {
    userAgent: navigator.userAgent,
    languages: (navigator.languages || []).join(','),
    timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
    screen: [screen.width, screen.height, screen.colorDepth].join('x'),
    pixelRatio: window.devicePixelRatio,
    cores: navigator.hardwareConcurrency || null,
    memory: navigator.deviceMemory || null,
    touchPoints: navigator.maxTouchPoints || 0
  };
}

Two notes. navigator.deviceMemory is only available in Chromium-based browsers, so it returns undefined elsewhere; the code stores null instead. And Chromium browsers have reduced the default user-agent string, moving details into User-Agent Client Hints, so the user agent alone says less than it used to.

Step 2: add a canvas rendering test

Rendering tests add most of the distinguishing power. The page draws text and shapes on an off-screen canvas and reads the result back. Font rendering, anti-aliasing and graphics hardware differ slightly between machines, so the output differs too:

function canvasSignal() {
  const canvas = document.createElement('canvas');
  canvas.width = 240;
  canvas.height = 60;
  const ctx = canvas.getContext('2d');
  if (!ctx) return 'no-canvas';
  ctx.textBaseline = 'top';
  ctx.font = '16px Arial';
  ctx.fillStyle = '#f60';
  ctx.fillRect(100, 1, 62, 20);
  ctx.fillStyle = '#069';
  ctx.fillText('fingerprint test 123', 2, 15);
  return canvas.toDataURL();
}

The canvas is never added to the page, so the visitor sees nothing. Similar tests exist for WebGL and audio processing; they follow the same pattern of render, read back and hash.

Step 3: combine and hash

A long data URL is unwieldy, so the usual last step is to hash everything into one short value. The Web Crypto API's crypto.subtle.digest supports SHA-256. According to MDN, it is only available in secure contexts, meaning pages served over HTTPS or from localhost:

async function sha256Hex(text) {
  const bytes = new TextEncoder().encode(text);
  const hash = await crypto.subtle.digest('SHA-256', bytes);
  return Array.from(new Uint8Array(hash))
    .map(b => b.toString(16).padStart(2, '0'))
    .join('');
}

async function getFingerprint() {
  const data = collectAttributes();
  data.canvas = canvasSignal();
  const keys = Object.keys(data).sort();
  const text = keys.map(k => k + '=' + data[k]).join('|');
  return sha256Hex(text);
}

getFingerprint().then(id => console.log('fingerprint', id));

Sorting the keys keeps the string in the same order every time, so the same inputs always produce the same hash.

Why the fingerprint keeps changing

Developers who try this quickly find that the hash is less stable than expected. Common reasons:

  • Browser updates change the user-agent string and sometimes the rendering output, so one update can produce a new hash.
  • Screen and window changes, such as an external monitor or a different zoom level, change screen and pixel-ratio values.
  • Deliberate protections. Browsers push back against fingerprinting. Brave adds small randomisation to canvas, WebGL and audio results so a browser looks different between websites and between sessions. Firefox and Safari restrict or generalise what scripts can read, and Tor Browser tries to make every user look the same.
  • Private or embedded contexts, where some APIs are limited or return defaults.

That is why a single full hash is a poor identifier on its own. Production systems generally compare signals one by one, accept partial matches and combine the result with other evidence. Our article on how to read a browser fingerprint test explains why consistency between values matters more than uniqueness.

The W3C guidance points out why fingerprinting is sensitive: unlike cookies, a fingerprint typically cannot be cleared or reset by the user, so it can re-identify people who deliberately removed cookies.

In the EU, the European Data Protection Board's Guidelines 2/2023 on the technical scope of Article 5(3) of the ePrivacy Directive (final version adopted in October 2024) confirm that the rule on accessing information in a user's device can apply to device fingerprinting techniques, not only to cookies. In practice this means asking for consent unless the processing is strictly necessary for a service the user requested. If personal data is involved, GDPR applies on top. Check the rules with your legal team before collecting fingerprints in production.

Good practice for legitimate uses such as fraud prevention or account security:

  • Collect only the signals you need, and say so in your privacy notice.
  • Store a hash, not the raw values, and set a retention period.
  • Never use a fingerprint to track people across unrelated websites.

What this means for ad operations

Advertising platforms read many of the same signals on their own pages, which is why ad teams care about them. Understanding them helps keep legitimately separate environments, such as different clients or team members, consistent and correctly set up. It is not a way around platform rules: Meta, Google and TikTok all prohibit techniques designed to evade their review or enforcement, and no fingerprint configuration changes that.

FAQ

Can JavaScript get a unique browser ID?

Not reliably. JavaScript can combine attributes and rendering tests into a hash that is often distinctive, but browser updates, display changes and built-in protections change it over time. Treat it as a probabilistic signal.

In the EU it usually does. The EDPB's Guidelines 2/2023 state that Article 5(3) of the ePrivacy Directive can cover device fingerprinting, so consent is required unless the processing is strictly necessary for a service the user asked for.

Why does crypto.subtle not work on my page?

crypto.subtle is only available in secure contexts. Serve the page over HTTPS, or test on localhost.

Can a browser block fingerprinting scripts?

Browsers reduce what scripts can learn. Brave randomises canvas, WebGL and audio output, Firefox and Safari restrict some APIs, and Tor Browser standardises many values so users look alike.

Find the right setup for your team

Discuss your account, IP and profile requirements with sales to confirm configuration and delivery.

Talk to sales