Back to Blog
Web WorkersFingerprintingAntidetectDetectionPrivacy

Fingerprinting in Web Workers: The Detection Method Nobody Talks About

Most antidetect tools only spoof the main browser thread. Web Workers run separate JavaScript contexts that can expose your real fingerprint. Here's how.

Raven Wallet Team

Stumbled onto something fun last month. Was testing a buddy's GoLogin setup, running profiles through CreepJS to see how they score. Main thread fingerprint looked clean. Canvas hash was different per profile. WebGL strings matched the spoofed config. Everything checked out.

Then I scrolled down to the Worker section.

Every single profile had the exact same navigator.hardwareConcurrency in the ServiceWorker context. His real CPU core count. Eight profiles, all showing 16 cores. On a machine that was supposed to be spoofed as having 4.

His setup was leaking like crazy and he had no idea.

Why Workers are a blind spot

Here's the thing most people miss. When you open a webpage, JavaScript runs in what's called the "main thread." That's where your DOM lives, where React renders, where 99% of browser fingerprinting libraries do their work.

But JavaScript also has these things called Web Workers. Three flavors:

  • Worker - a background thread for heavy computation
  • SharedWorker - shared between multiple tabs
  • ServiceWorker - a persistent proxy that intercepts network requests

Each one gets its own execution context. Its own navigator object. Its own globals. And here's the kicker - they don't inherit spoofing from the main thread. At all.

So if your antidetect browser patches navigator.hardwareConcurrency on the main page but doesn't touch Worker contexts, a detection script just needs to spin up a quick Worker and ask it the same question. Real answer comes right back.

What gets exposed

Let me walk through what a detection script can pull from an unspoofed Worker.

navigator.hardwareConcurrency is the easy one. Most antidetect tools change this on the main page. Few change it in Workers. Detection script creates a Worker, queries navigator, compares. Mismatch = you're spoofed.

navigator.userAgent and navigator.platform - same deal. If your main page says macOS but your Worker says Windows, game over.

OffscreenCanvas is where it gets nasty. Workers can't access the regular DOM canvas, but they can create an OffscreenCanvas and do the exact same rendering tests. Draw some text, grab the pixels, hash them. If the canvas fingerprint from a Worker doesn't match the one from the main thread, something's wrong. And if it matches your real hardware instead of the spoofed one, even worse.

AudioContext works in Workers too. OfflineAudioContext, oscillator nodes, the whole thing. Run the same audio fingerprint test in a Worker context and compare.

The Blob URL trick

This one's clever. Saw it in CreepJS source code and went "oh, that's evil."

Normal Workers load from a URL. Like new Worker('/my-script.js'). An antidetect extension could theoretically intercept requests to that URL and inject spoofing code.

But what about this:

const code = `postMessage(navigator.hardwareConcurrency)`;
const blob = new Blob([code], { type: 'application/javascript' });
const url = URL.createObjectURL(blob);
const worker = new Worker(url);

The script creates JavaScript code as a string, wraps it in a Blob, generates a temporary URL, and launches a Worker from it. No file to intercept. No URL to block. The code exists only in memory.

If your antidetect tool doesn't intercept Blob construction and URL.createObjectURL, this Worker runs completely clean. No spoofing. Real fingerprint.

Gets worse. data: URLs work too:

const worker = new Worker('data:application/javascript,' +
  encodeURIComponent('postMessage(navigator.hardwareConcurrency)'));

Same idea, different delivery method. Most extension-based antidetect tools miss both of these.

How CreepJS actually does it

Spent a weekend reading through CreepJS internals. Not the most fun weekend, gonna be honest, but I learned a lot.

Their approach is methodical. They run the same fingerprint collection in multiple contexts and cross-reference everything. Main thread, Worker, SharedWorker, ServiceWorker. If all four return consistent values, you're probably clean. If any one differs, they flag it.

They specifically test:

  1. Hardware values (hardwareConcurrency, deviceMemory)
  2. User agent string and platform
  3. Canvas rendering via OffscreenCanvas (when available)
  4. Timezone and language settings
  5. AudioContext fingerprint

The genius part is the comparison logic. They don't just check if Workers are spoofed. They check if Workers match the main thread. Even if you spoof both but with different noise seeds, the mismatch itself is a detection signal.

SharedWorker shenanigans

SharedWorkers add another layer. They're shared between tabs, so if you have two tabs open in the same profile, they connect to the same SharedWorker instance. A detection site could leave a SharedWorker running and check its fingerprint against whatever your current page reports.

Not many sites use this technique yet. But the capability is there. And the anti-fraud companies building enterprise detection (PerimeterX, Shape, DataDome) absolutely use it.

The ServiceWorker problem

ServiceWorkers are the hardest to deal with. They persist. They run even when your tab is closed. They intercept network requests.

A site registers a ServiceWorker on first visit. That SW caches your real fingerprint. Next time you visit - even with a different profile - the SW is still there with the cached data. Obviously you need proper session isolation to prevent this, but plenty of setups skip this.

The harder problem: injecting spoofing code into a ServiceWorker that a third-party site registered. You can't modify the SW's source code. You can't inject a content script into it. You'd have to intercept the registration itself and wrap the SW URL.

How proper interception works

The right approach is what I'd call "early interception." Before any page script runs - literally at document_start - you override the constructors.

You save references to the original Worker, SharedWorker, and Blob constructors. Then you replace them with wrapped versions that inject your spoofing code into every Worker that gets created.

For file URL Workers, you prepend your spoofing script via importScripts().

For Blob Workers, you parse the Blob content, prepend spoofing code, and create a new Blob.

For data: URL Workers, you decode the URL, inject code, re-encode.

For Reflect.construct - because yeah, scripts try to bypass your overrides by using Reflect.construct(originalWorker, args) - you override that too.

It's a game of whack-a-mole, honestly. But if you do it early enough and thoroughly enough, you can cover the gaps. The key word is "early." If even one script saves a reference to the original Worker constructor before your code runs, it's over. That's why this needs to happen in the MAIN world at document_start, not from an extension's isolated world.

What to actually check

Next time you're evaluating your antidetect setup, don't just look at the main thread scores. Go to CreepJS, scroll to the Workers section, and compare.

Open your browser's DevTools, create a Worker inline:

const w = new Worker(URL.createObjectURL(
  new Blob([`postMessage({
    cores: navigator.hardwareConcurrency,
    platform: navigator.platform,
    ua: navigator.userAgent
  })`])
));
w.onmessage = e => console.log('Worker says:', e.data);

Compare those values to what the main thread reports. If they match your spoofed config, good. If they match your real hardware, you've got a problem.

Check out our testing checklist for a full walkthrough of what to verify.

The uncomfortable truth

Most antidetect tools don't handle Worker fingerprinting properly. The ones charging $100/month. The free ones. Even some well-known names. Worker interception is hard to implement, easy to break, and most users never check it.

If you're running multiple crypto wallets through browser profiles and your Workers are leaking real hardware info, you're giving detection systems exactly what they need to link your accounts. Different IPs, different cookies, different canvas hashes - but identical Worker fingerprints across all profiles.

That's not isolation. That's a costume with a name tag.

Sound paranoid? Maybe. But I've seen people lose accounts over exactly this. The platforms doing multi-account detection are getting smarter every quarter. Worker fingerprinting isn't some theoretical attack - it's in production detection systems right now.

Fair enough?