JustBrowser
Tutorials12 min read

Extension Detection: How Sites Enumerate Your Installed Add-Ons and Flag Wrapper Browsers

JustBrowser Platform Team·

Three weeks ago I was debugging why a batch of antidetect profiles kept hitting CAPTCHAs on a travel site. Fingerprints looked clean. Proxies were residential. Canvas, WebGL, audio — all passing. Then someone in our Discord asked: "Did you check Extension Fingerprints?"

I hadn't. I loaded extensionfingerprints.com in one of the flagged profiles. Fourteen detected extensions. Fourteen. The antidetect browser itself was showing up as a Chrome extension with five probe-able resources. The password manager I'd forgotten to disable. Three more from the browser's "helpful" built-in features I didn't know existed.

Each installed extension is a fingerprinting signal. Combine enough of them and you've got a unique identifier. We'd been spending hours on canvas spoofing while ignoring a detection surface hiding in plain sight.

What We're Building

By the end of this tutorial, you'll understand:

  1. How web_accessible_resources probing works at the protocol level
  2. Why DOM injection from content scripts creates detectable artifacts
  3. The timing attacks that reveal JavaScript-based spoofing
  4. How to audit your antidetect profiles for extension leaks
  5. Why native-engine antidetect browsers don't have this attack surface

You'll walk away with a practical checklist for auditing profiles — and a clearer understanding of why architecture matters more than features when detection services actively hunt for extension signals.

Prerequisites

  • An antidetect browser profile (JustBrowser, Multilogin, GoLogin, whatever you're using)
  • Basic understanding of Chrome extension structure (manifest.json, content scripts)
  • 20 minutes
  • Access to extensionfingerprints.com and CreepJS

If you want background on how detection services cross-validate fingerprint signals, the CreepJS testing walkthrough covers the methodology.

Step 1: Understanding web_accessible_resources

Every Chrome extension has a manifest.json file. One field matters for detection: web_accessible_resources.

{
  "name": "Some Extension",
  "version": "1.0",
  "web_accessible_resources": [
    {
      "resources": ["logo.png", "inject.js", "style.css"],
      "matches": ["<all_urls>"]
    }
  ]
}

Those listed resources become accessible to any webpage via URLs like:

chrome-extension://abcdefghijklmnop/logo.png

Here's the thing. A site can silently probe that URL. If the resource loads — if the fetch returns 200 instead of failing — the extension is installed. No permission prompts. No user interaction. The page just checks and knows.

Detection services maintain databases of extension IDs and their known resources. Probe 500 extensions. Any that respond? Added to your fingerprint. The combination of installed extensions is often unique enough to identify individuals across sessions. Kind of annoying that something as innocuous as a password manager can blow your cover, but here we are.

Run this yourself:

// Check if uBlock Origin is installed (one of its known resources)
fetch('chrome-extension://cjpalhdlnbpafiamejdnhcphjbkeiagm/web_accessible_resources/scriptlets/json-prune.js')
  .then(r => r.ok ? console.log('uBlock installed') : console.log('Not installed'))
  .catch(() => console.log('Not installed or blocked'));

(If you're seeing "uBlock installed" and you have it — that's the point. The page knows.)

Step 2: The DOM Injection Problem

Content scripts are the second detection surface.

When an extension injects a content script, it often modifies the DOM — adding elements, modifying styles, or overriding JavaScript functions. These modifications leave artifacts.

Extension-based antidetect browsers rely on content scripts to spoof fingerprint values. They inject JavaScript that overrides navigator.userAgent, canvas.toDataURL, WebGLRenderingContext.getParameter. Standard approach.

But that injection is detectable:

Injected elements: Some extensions add hidden divs, iframes, or script tags. Sites enumerate unexpected elements and flag them. A div with an ID containing "extension" or class names matching known antidetect tools? Red flag.

CSS modifications: Content scripts sometimes inject stylesheets. Sites compute styles on known elements and compare against baselines. Unexpected CSS rules suggest extension presence.

Prototype tampering: This is the big one. When you override navigator.userAgent via JavaScript:

Object.defineProperty(navigator, 'userAgent', {
  get: () => 'spoofed value'
});

The property descriptor changes. Object.getOwnPropertyDescriptor(navigator, 'userAgent') now shows different characteristics than the native implementation. Detection services check for this. CreepJS calls them "lies."

The extension-based antidetect browser is modifying the DOM and prototypes to hide signals — but the modifications themselves become signals. That's the paradox. I've seen GoLogin profiles fail detection not because of fingerprint mismatches, but because the GoLogin extension's own DOM artifacts were enumerated.

Step 3: Timing Attacks on JavaScript Overrides

This is the part most antidetect vendors don't want you to know about.

Native property access is fast. Reading navigator.userAgent on an unmodified browser takes maybe 0.01ms. A JavaScript getter override takes 0.1-0.3ms. That's a 10-30x timing difference — detectable.

// Timing attack demonstration
function measurePropertyAccess(obj, prop, iterations = 1000) {
  const start = performance.now();
  for (let i = 0; i < iterations; i++) {
    const _ = obj[prop];
  }
  return (performance.now() - start) / iterations;
}

const uaTime = measurePropertyAccess(navigator, 'userAgent');
console.log(`userAgent access: ${uaTime.toFixed(4)}ms`);
// Native: ~0.001ms. Overridden: ~0.05ms or higher.

Detection services run thousands of property access timings and compare against baselines. Consistent timing inflation across known-spoofed properties? Flagged.

And you can't fake being fast. That's what makes this devastating for extension-based antidetect tools. They're fundamentally adding layers that detection services time.

Some extension-based tools try to add random delays to mask the pattern. Doesn't work. The variance itself becomes a signal. Real browsers have consistent, low-variance timing. Fake delays create noise that's arguably more detectable than doing nothing.

I spent a weekend trying to build timing obfuscation into a content script once (don't ask). Everything I tried either failed the timing check directly or created statistical patterns that were arguably more detectable than doing nothing. The architecture is just wrong for this use case.

Step 4: Auditing Your Profiles

Here's the practical workflow I use before deploying any profile to production accounts.

Step 4a: Run Extension Fingerprints

Open extensionfingerprints.com in your antidetect profile. Wait for it to probe. It'll show detected extensions.

What you want: Zero detected extensions — or only extensions you've deliberately whitelisted for the persona (rare).

What you'll often see: The antidetect browser itself detected. Built-in browser features exposing resources. That password manager you forgot was enabled.

Step 4b: Check CreepJS Lies Section

Navigate to abrahamjuliot.github.io/creepjs. Scroll to "Lies."

Red entries mean prototype tampering was detected. Common failures:

  • "WorkerNavigator mismatch" — your main-thread spoofing doesn't reach Web Workers
  • "PrototypeLies detected" — detection of modified property descriptors
  • Timing anomalies in the fingerprint consistency checks

The CreepJS guide breaks down each section. For extension detection specifically, you're looking for any signal that JavaScript injection is occurring.

Step 4c: Inspect DOM for Injected Elements

Open DevTools. Run:

// Look for elements with IDs or classes containing common extension patterns
document.querySelectorAll('[id*="extension"], [id*="inject"], [class*="extension"]')
  .forEach(el => console.log(el));

// Check for unexpected iframes
document.querySelectorAll('iframe').forEach(f => console.log(f.src));

// Look at scripts not from the page itself
document.querySelectorAll('script[src]').forEach(s => {
  if (!s.src.startsWith(location.origin)) console.log('External:', s.src);
});

Any elements you didn't expect? That's extension injection. Might be your antidetect tool. Might be something else. Either way, it's a detection surface.

Step 4d: Test Property Access Timing

function timeProperty(obj, prop) {
  const runs = [];
  for (let i = 0; i < 100; i++) {
    const start = performance.now();
    for (let j = 0; j < 1000; j++) { const _ = obj[prop]; }
    runs.push(performance.now() - start);
  }
  return {
    avg: (runs.reduce((a,b) => a+b) / runs.length).toFixed(3),
    variance: (Math.sqrt(runs.map(x => Math.pow(x - runs.reduce((a,b) => a+b)/runs.length, 2)).reduce((a,b) => a+b) / runs.length)).toFixed(3)
  };
}

console.log('navigator.userAgent:', timeProperty(navigator, 'userAgent'));
console.log('navigator.platform:', timeProperty(navigator, 'platform'));
console.log('screen.width:', timeProperty(screen, 'width'));

Native properties show low, consistent times with low variance. Overridden properties show higher times and/or higher variance. If navigator.userAgent is 10x slower than screen.width — something's modifying it.

Step 5: Why Native Engines Don't Have This Problem

This is where the architecture distinction becomes concrete.

Native-engine antidetect browsers — JustBrowser, Multilogin's MLA2, and a few others — don't use extensions or content scripts. They modify the Chromium source code at the C++ level.

No extension manifest. No web_accessible_resources to probe. No content scripts injecting DOM elements. No JavaScript overrides creating timing signatures.

When a native-engine browser reports a canvas fingerprint, it's generating that fingerprint from the modified rendering pipeline. The value is genuine as far as JavaScript can tell. There's no override to time because the value was never different.

The property access takes 0.001ms because it's reading a native value — it just happens to be a native value that's different from what the hardware would produce. At the property-access layer there is nothing to time or descriptor-check, so this particular surface looks the same as a real device.

I'm obviously biased here (I work on JustBrowser). But this isn't marketing. It's just how the architectures work. Extension-based tools have detection surfaces that native engines don't. Full stop.

(That said — native engines have their own headaches. Keeping up with Chromium updates is a slog. Ensuring spoofed values stay consistent across all 40+ parameters that matter takes real work. Building APIs that play nice with Playwright and Puppeteer isn't trivial. The engineering is harder. That's why most antidetect tools take the easier extension route and accept the detection trade-off. I get it — I just wish they'd be honest about the limitations.)

Common Errors and Fixes

Error: Extension Fingerprints shows detected extensions but I disabled them

Cause: Browser built-ins, PDF viewers, or "features" that ship as internal extensions. Some antidetect browsers bundle their own extensions even when claiming native implementation.

Fix: Check your antidetect browser's settings for internal features. Disable PDF viewer, translate features, anything that might be extension-based. If the antidetect tool itself is detected — it's not actually native-engine, regardless of marketing claims. Consider switching to a native solution.

Error: CreepJS shows prototype tampering but my antidetect browser claims native support

Cause: Mixed architecture. Some vendors use native modification for some parameters and extension injection for others. The extension-injected parameters get flagged.

Fix: Run the timing tests from Step 4d. If some properties are slow and others aren't, you've got a hybrid architecture. The slow properties are JavaScript overrides. Either accept the detection risk or switch to a fully native tool.

Error: DOM shows injected elements I can't identify

Cause: Could be browser extensions you forgot about. Could be the antidetect browser's infrastructure. Could be a malicious extension you didn't install intentionally.

Fix: Disable all extensions. Test again. If elements persist, they're from your antidetect browser itself. If they disappear, re-enable extensions one at a time to identify the culprit.

Error: Timing tests pass but I'm still getting flagged

Cause: Extension detection is one surface among many. IP quality, behavioral signals, TLS fingerprints, and cross-session linking all contribute. Passing extension checks isn't enough on its own. Detection vendors like ClickzProtect cross-reference multiple signals — extension enumeration is just one input.

Fix: Work through the full verification checklist. JA4 TLS fingerprinting, proxy quality scoring, behavioral consistency. Extension enumeration is one piece of a larger detection stack.

Next Steps

Once your profiles pass extension enumeration checks:

  1. Verify the rest of the fingerprint stack — CreepJS, WebGL, audio, fonts
  2. Check TLS fingerprints — your browser's network-layer identity should match the claimed platform
  3. Audit proxy quality — residential, clean reputation, geo-matched to profile timezone
  4. Document passing configurations — create profile templates from verified setups

For teams running automation, the Playwright/Puppeteer integration guide covers how to maintain fingerprint integrity under programmatic control — which introduces its own detection surfaces beyond extension enumeration.

Extension detection is table stakes now. Every serious detection service checks for it. The profiles that survive are the ones running on architectures that don't have extension surfaces to probe. Native engine isn't optional anymore — it's baseline.

Frequently Asked Questions

What are web_accessible_resources and why do they matter for detection?

web_accessible_resources is a Chrome extension manifest field that lists files accessible to web pages. Sites can probe these URLs (chrome-extension://[extension-id]/[file]) to check if specific extensions are installed. If the resource loads, the extension is present. This creates a fingerprinting vector because each installed extension combination is relatively unique.

Can Manifest V3 extensions still be detected through resource probing?

Yes, but with limitations. Manifest V3 requires extensions to explicitly declare accessible resources and can restrict them to specific URL patterns. However, many extensions still expose resources for functionality. The detection shifts toward DOM injection patterns, timing differences, and behavioral analysis rather than pure resource enumeration.

Why do extension-based antidetect browsers fail extension detection tests?

Extension-based antidetect browsers inject their spoofing logic via content scripts — which are themselves detectable extensions. The antidetect extension's own web_accessible_resources can be probed, its DOM modifications leave artifacts, and its JavaScript overrides create timing signatures. You're trying to hide extensions using an extension, which is inherently detectable.

How can I check if my antidetect browser leaks extension signals?

Run your profile against Extension Fingerprints (extensionfingerprints.com), check CreepJS for prototype tampering, and inspect your DOM for injected elements or modified prototypes. Look for elements you didn't create, timing anomalies in property access, and mismatched CSS that suggests content script injection.


Try JustBrowser

Native Chromium antidetect browser — not extension-based. Real C++ engine patches at the canvas / WebGL / audio / font / screen layer, so 40+ identity parameters are genuine, not faked. REST API for Playwright, Puppeteer, Selenium. $9.99/month or $99.99/year. 7-day free trial, card required — cancel any time in the seven days and you are not charged. Unlimited profiles.

Get started → · How it differs from Multilogin / GoLogin / AdsPower

Ready to manage multiple accounts?

Seven days free, then $9.99/month — one plan, everything included.

We'd like to use Google Analytics, a Google service, to understand how our website is used. It sets two cookies in your browser and runs only if you click Accept. You can change your choice at any time with Cookie settings. Cookie Policy

Sign-in cookies and the cookie that remembers this choice are always on; the website needs them to work.

Google Analytics, a Google service, helps us understand how our website is used. It sets two cookies, _ga and _ga_TVZHQ99TZW. It is now onoff in this browser. If your browser sends a Global Privacy Control or Do Not Track signal, it stays off. Cookie Policy