Preloader
Others
  • Estimated reading time: 6 Minutes

Understanding browser fingerprinting: How websites detect automated browsers

Understanding browser fingerprinting: How websites detect automated browsers

If you have ever written a Puppeteer or Selenium script and watched it get blocked within seconds, even though your IP address and request headers looked completely normal, you have run into browser fingerprinting. No captcha, no rate limit warning, just a silent block or a redirect to a verification page.

The site did not "guess" you were a bot. It measured your browser.

This is also the exact problem that antidetect browsers were built to solve. Instead of running one browser and hoping a patch or two hides the obvious signs of automation, an antidetect browser generates a full, internally consistent fingerprint for each profile, so every session looks like it belongs to a different real device rather than a script pretending to be one.

Modern websites use dozens of small technical signals, unrelated to cookies or IP addresses, to build a kind of digital signature for every visitor. This article walks through how fingerprinting actually works, shows some of the underlying code, and explains why tools built specifically around fingerprint management exist in the first place.

Fingerprinting vs. Cookies and IP Tracking

Cookies and IP addresses are the tracking methods most developers think of first, and they are also the easiest to defeat. Clear your cookies, rotate your proxy, and the tracking resets.

Fingerprinting works differently. It reads properties that come from your actual hardware, operating system, GPU driver, installed fonts, and browser configuration, then combines them into a single hash. You do not need to store anything on the client for this to work, which is exactly why it is so hard to shake off. Clearing your cache does nothing, because there was never anything stored there to begin with.

The Main Signals Sites Collect

Canvas Fingerprinting

The canvas API lets a page draw shapes, text, and gradients, then read back the resulting image as raw pixel data. Because font rendering, anti-aliasing, and GPU processing differ slightly across devices, two machines rendering the exact same canvas instructions will produce subtly different output.

function getCanvasFingerprint() {
  const canvas = document.createElement('canvas');
  const ctx = canvas.getContext('2d');

  ctx.textBaseline = 'top';
  ctx.font = '14px Arial';
  ctx.fillStyle = '#f60';
  ctx.fillRect(0, 0, 100, 20);
  ctx.fillStyle = '#069';
  ctx.fillText('fingerprint test', 2, 2);

  return canvas.toDataURL();
}

That returned string is hashed and used as one input to the overall fingerprint. Run this in Chrome on two different machines and you will almost certainly get two different hashes.

WebGL Fingerprinting

WebGL exposes information about the actual graphics card and driver in use, which is far more specific than a generic user agent string.

function getWebGLInfo() {
  const canvas = document.createElement('canvas');
  const gl = canvas.getContext('webgl');
  const debugInfo = gl.getExtension('WEBGL_debug_renderer_info');

  return {
    vendor: gl.getParameter(debugInfo.UNMASKED_VENDOR_WEBGL),
    renderer: gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL)
  };
}

This can return something like ANGLE (NVIDIA, NVIDIA GeForce RTX 3060 Direct3D11 vs_5_0 ps_5_0), which narrows down the visitor's hardware considerably.

AudioContext Fingerprinting

Even audio processing is not immune. Running a signal through OfflineAudioContext and reading the output produces values that vary slightly based on the audio stack and hardware.

function getAudioFingerprint() {
  return new Promise((resolve) => {
    const context = new OfflineAudioContext(1, 44100, 44100);
    const oscillator = context.createOscillator();
    oscillator.type = 'triangle';
    oscillator.frequency.setValueAtTime(10000, context.currentTime);

    const compressor = context.createDynamicsCompressor();
    oscillator.connect(compressor);
    compressor.connect(context.destination);

    oscillator.start(0);
    context.startRendering();

    context.oncomplete = (event) => {
      const output = event.renderedBuffer.getChannelData(0);
      const sum = output.slice(0, 100).reduce((a, b) => a + Math.abs(b), 0);
      resolve(sum.toString());
    };
  });
}

Navigator Properties

This is the layer that trips up automation tools most often, because it is the cheapest for a site to check and the easiest for a script to accidentally reveal.

console.log(navigator.webdriver);   // true in unpatched Selenium/Puppeteer
console.log(navigator.plugins.length); // often 0 in headless browsers
console.log(navigator.languages);   // sometimes empty in headless mode

A single if (navigator.webdriver === true) check on the server side, or client side before rendering the page, is enough to flag a huge share of naive automation scripts.

Fonts, Screen Size, and Timezone Mismatches

Sites also enumerate installed fonts, screen resolution, and color depth, then cross check them against the timezone and locale reported by the browser. A visitor whose IP resolves to Vietnam but whose system timezone is set to UTC, with an English-only font list, is an easy pattern to flag, regardless of how clean the network layer looks.

A Quick Test: How Exposed Is Your Automated Browser?

Here is a minimal Puppeteer script that launches a default headless browser and checks a few of the signals above.

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch({ headless: 'new' });
  const page = await browser.newPage();

  const result = await page.evaluate(() => {
    return {
      webdriver: navigator.webdriver,
      pluginsLength: navigator.plugins.length,
      languages: navigator.languages,
      userAgent: navigator.userAgent
    };
  });

  console.log(result);
  await browser.close();
})();

Run this against a default Puppeteer install and webdriver comes back true, pluginsLength is often 0, and the user agent string frequently still contains the word "HeadlessChrome" depending on the version. Any one of these is enough for a basic detection script to flag the session before it even loads the page content you actually wanted to scrape.

You can see the same idea applied more thoroughly on public fingerprint testing tools like CreepJS, which combines dozens of these checks into a single detection score.

Fixing This at the Technical Level

Patching individual properties. Tools like puppeteer-extra-plugin-stealth override navigator.webdriver, fake the plugins array, and normalize a few other properties automatically. This closes the most obvious gaps quickly, but it is a moving target. Every time a site adds a new check, or Chromium changes how a property is exposed internally, the patch can fall behind.

const puppeteer = require('puppeteer-extra');
const StealthPlugin = require('puppeteer-extra-plugin-stealth');
puppeteer.use(StealthPlugin());

Spoofing the full fingerprint at the browser level. Instead of patching individual JavaScript properties after the fact, another approach is to generate an internally consistent fingerprint profile for the whole browser session: canvas hash, WebGL renderer string, audio output, fonts, and timezone are all set to match each other and stay stable across a profile's lifetime, while still varying from one profile to another. This is the core mechanism behind dedicated antidetect browser tooling, and it matters most in situations like QA testing across many simulated devices, or managing multiple genuinely separate accounts for legitimate business operations, where a single machine needs several isolated and internally consistent browser identities rather than one script trying to look like everyone at once.

The trade off is straightforward. Manual patching is free and fine for small, one off scraping jobs. A dedicated multi profile setup makes more sense once you are managing dozens of isolated sessions and need each one to hold up under closer scrutiny over time, since inconsistency between signals, not any single signal on its own, is usually what gets a session flagged.

Conclusion

Browser fingerprinting is not a single check you can patch once and forget. It is a combination of dozens of small, mostly independent signals, and detection systems get better at cross referencing them every year. There is no permanent fix, only an ongoing back and forth between what sites measure and what automation tools account for.

If you want to see exactly what your own browser exposes right now, tools like the EFF's Cover Your Tracks are a good starting point, they run many of the same checks described above and show you the resulting fingerprint in plain language.

Understanding these mechanics is useful even if you never touch automation. Any developer building analytics, fraud detection, or bot mitigation ends up working with some version of the same techniques from the other side of the fence.

Related articles
Is AIMath-Solver.net a Reliable Math Solver for Students?
7 Sep, 2026
  • Estimated reading time: 6 Minutes
How Disinformation Spreads Online
7 Sep, 2026
  • Estimated reading time: 3 Minutes
How IPTV Streaming Is Changing the Way People Watch TV
7 Sep, 2026
  • Estimated reading time: 8 Minutes
How Developers and Marketers Can Optimize Websites for AI Search
7 Sep, 2026
  • Estimated reading time: 6 Minutes
AI Software Development Company: Services, Process & Pricing
7 Sep, 2026
  • Estimated reading time: 5 Minutes
Weekly trending
Is AIMath-Solver.net a Reliable Math Solver for Students?
7 Sep, 2026
  • Estimated reading time: 6 Minutes
How Disinformation Spreads Online
7 Sep, 2026
  • Estimated reading time: 3 Minutes
How IPTV Streaming Is Changing the Way People Watch TV
7 Sep, 2026
  • Estimated reading time: 8 Minutes
How Developers and Marketers Can Optimize Websites for AI Search
7 Sep, 2026
  • Estimated reading time: 6 Minutes
Our Sponsors

Our blog is proudly supported by industry-leading sponsors.