Resource probing and behavioural detection
The most direct method targets those shipped files. An extension lists files as web_accessible_resources in its manifest (its config file), which makes them reachable at a fixed address: chrome-extension://<extension-id>/path. Every extension has a stable ID, so a page can try to fetch() or load an <img> at a known ID and path - if it loads, the extension is installed. Manifest V3 (the newer extension format) made this harder by giving each origin a randomised UUID instead of a fixed ID, but small differences in timing and error messages still leak an extension's presence in many cases.
Indirect methods watch for what an extension does rather than what it ships: ad blockers remove elements with bait class names, password managers inject icons into form fields, and grammar checkers add overlays. A site plants bait and watches whether it gets altered, the way you might leave a marked item out to see if someone touches it.
Why it matters for bot detection
For anti-bot purposes the signal cuts both ways. A real human profile usually carries a handful of common extensions (uBlock Origin, a password manager). A freshly spun headless profile - a browser launched with no visible window and a blank slate - carries none, which, combined with empty history and a default font set, paints a clear automation picture. Conversely, some automation frameworks inject their own helper extensions whose resources are detectable directly. The realistic profile for scraping mirrors a believable human: a small, plausible extension set rather than a sterile blank slate.
Avoiding extension tells in automation
Two things give automation away here. First, automation-specific extensions and helpers (old Selenium IDE artifacts, injected helper scripts) expose web_accessible_resources that a page can probe for with a simple image or fetch load. Second, the absence of any extension at all - no ad blocker, no password manager, none of the resource-blocking behaviour a real user's browser exhibits - is itself a weak signal that you are a clean automation profile.
The fix is to drive the browser through the DevTools Protocol (Chrome's built-in remote-control interface) rather than injected extensions, so there are no chrome-extension:// resources to fingerprint, and to let the profile look ordinarily "lived-in" rather than pristine. Tools like Camoufox and managed scraping backends aim for this middle ground: no automation-specific extensions to detect, but a realistic, coherent profile rather than an obviously empty one.
