9 Headless Browsers Tested Against Three-Phase Bot Detection

The detect-bot project runs three detection phases: static browser fingerprinting (Phase 1), environment consistency cross-referencing (Phase 2), and behavioral interaction analysis (Phase 3). A test harness ran 9 headless browser configurations through all three phases against the live detect-bot test page. Chrome 148 was installed on the test server to remove binary-availability as a variable.
| Browser | Engine | Phase 1+2 | Phase 3 |
|---|---|---|---|
| Vanilla Playwright | Chromium | 0/100 BOT | 100/100 |
| Stealth Playwright | Chromium | 0/100 BOT | 100/100 |
| Full Stealth Playwright | Chromium | 0/100 BOT | 100/100 |
| Patchright 1.60 | Chromium | 0/100 BOT | 100/100 |
| nodriver 0.50.3 | Chromium CDP | -35/100 BOT | 100/100 |
| SeleniumBase UC 4.49 | Chromium UC | -20/100 BOT | 100/100 |
| CloakBrowser 0.3.31 | Chromium 146 | 30/100 SUSPICIOUS | 100/100 |
| Camoufox 0.4.11 | Firefox C++ | 35/100 SUSPICIOUS | 100/100 |
| invisible-playwright 0.2.0 | Firefox 150 | 35/100 SUSPICIOUS | 100/100 |
Phase 1 Signal Breakdown
| Signal | Vanilla | Stealth | Full St. | Patchright | nodriver | SB UC | Cloak | Camoufox | invis.pw |
|---|---|---|---|---|---|---|---|---|---|
| navigator.webdriver | true | patched | patched | true | true | true | false ✓ | false ✓ | false ✓ |
| navigator.plugins | 0 | 0 | 5 ✓ | 0 | 5 ✓ | 5 ✓ | 5 ✓ | 5 ✓ | 5 ✓ |
| window.chrome | missing | missing | missing | missing | missing | missing | 3 keys ✓ | N/A | N/A |
| chrome.runtime | missing | missing | missing | missing | missing | missing | missing | N/A | N/A |
| performance.now() | sub-ms ✓ | integer | integer | sub-ms ✓ | integer | sub-ms ✓ | integer | integer | integer |
| Stack trace | clean ✓ | eval origin | eval origin | clean ✓ | clean ✓ | clean ✓ | eval origin | clean ✓ | clean ✓ |
| Notification.permission | denied | denied | denied | default ✓ | default ✓ | denied | denied | denied | denied |
Phase 2 Signal Breakdown
| Signal | Vanilla | Stealth | Full St. | Patchright | nodriver | SB UC | Cloak | Camoufox | invis.pw |
|---|---|---|---|---|---|---|---|---|---|
| GPU Renderer | ANGLE ✓ | ANGLE ✓ | SwiftShader | ANGLE ✓ | ANGLE ✓ | ANGLE ✓ | RTX 4060 ✓ | spoofed ✓ | spoofed ✓ |
| GPU vs UA Platform | match ✓ | match ✓ | macOS + SwShader | match ✓ | match ✓ | match ✓ | match ✓ | match ✓ | match ✓ |
| Timezone vs Locale | match ✓ | match ✓ | match ✓ | match ✓ | UTC + en-US | match ✓ | UTC + en-US | UTC + en-US | UTC + en-US |
| Language Depth | 2+ ✓ | 2+ ✓ | 2+ ✓ | 2+ ✓ | 2+ ✓ | 2+ ✓ | single | 2+ ✓ | single |
| Browser Chrome | present ✓ | present ✓ | present ✓ | present ✓ | missing | present ✓ | present ✓ | present ✓ | present ✓ |
| Viewport | 1280 ✓ | 1280 ✓ | 1280 ✓ | 1280 ✓ | 780×580 | 1280 ✓ | 1280 ✓ | 1280 ✓ | 1280 ✓ |
Phase 3 behavioral scores were 100/100 across all browsers. Playwright's .click() and .type() methods generate proper mousemove and keydown events - the behavioral phase is designed to catch AI agents that teleport to click coordinates and paste-fill forms, not CDP-based automation that moves the cursor.
Phase 1+2: Static Fingerprints
Phase 1 checks the browser's JavaScript surface - navigator.webdriver, plugin arrays, window.chrome object, performance.now() precision, stack trace analysis, and DevTools Protocol detection. Phase 2 cross-references environment signals - does the GPU renderer match the reported platform? Does the timezone match the locale?
The Playwright variants, Patchright, nodriver, and SeleniumBase all scored in the BOT range. The three Chromium-based Playwright variants hit 0/100 on navigator.webdriver=true (vanilla), a detectable undefined override plus empty plugins (stealth), and SwiftShader GPU on a macOS user agent (full stealth). Patchright showed the same detection surface.
nodriver scored -35/100 with flags on navigator.webdriver, integer-only performance.now(), no browser chrome, a viewport mismatch (outer 780×580 vs inner 780×493 from a missing window frame), and UTC timezone. SeleniumBase UC's undetected-chromedriver mode only reached -20/100 - navigator.webdriver remained true in the CDP connection, window.chrome was missing, and Notification.permission defaulted to denied.
CloakBrowser (Chromium 146, 57 patches), Camoufox (Firefox fork, C++ level patches), and invisible-playwright (Firefox 150, 1,089 GitHub stars) reached 30-35/100. All three escaped the BOT verdict but couldn't hide from the signal combination. CloakBrowser spoofed the GPU to an NVIDIA RTX 4060 - passing the Phase 2 GPU consistency check - but its single language entry, eval-origin stack traces, integer-only performance.now(), and UTC timezone accumulated penalties. Camoufox and invisible-playwright tied at 35/100 - both Firefox forks with similar detection profiles: integer performance.now() timing, UTC timezone with regional English locale, and Notification.permission set to denied. invisible-playwright patches navigator.webdriver and the GPU renderer at the Firefox 150 source level but can't hide from OS-level timing and timezone signals.
Phase 3: Behavioral Analysis
Phase 3 tracks mouse movement continuity, scroll patterns, and keyboard interaction timing - the techniques described in FP-Agent[^1], which achieved 0.9993 F1 on AI agent detection using behavioral signals alone. It checks for teleporting clicks (no mousemove within 250ms), scroll events arriving in discrete bursts with uniform intervals, and forms filled via paste events rather than keystrokes.
Every browser scored 100/100 on Phase 3. Playwright's .click() method moves the cursor to the target before clicking - it generates mousemove events. .type() fires keydown and keyup with realistic inter-key latency. The harness didn't scroll programmatically or paste-fill forms. Phase 3 is designed to catch AI agents - Operator, Claude Computer Use, Manus - that teleport to click coordinates and fill forms without generating intermediate events. None of the tested automation tools exhibit that behavior.
The Two Detection Surfaces
Phase 1+2 and Phase 3 target different automation styles. Playwright-style automation controls a real browser via CDP - it patches browser attributes to hide itself but moves the mouse, types keystrokes, and scrolls like a human. The behavioral layer sees a human. The static fingerprint layer sees a bot.
The anti-detection browsers (CloakBrowser, Camoufox, invisible-playwright) close the static fingerprint gap. They patch at the C++/source level so navigator.webdriver, plugins, and GPU renderer return realistic values. But they can't patch operating-system-level signals - performance.now() precision is a headless-mode artifact below the browser, and the system timezone is set by the host.
The FP-Agent paper showed that Cloudflare's browser fingerprinting caught 1 of 7 AI agents tested. Behavioral analysis caught all 7. Those agents ran on real Chrome with human-like browser fingerprints - Phase 1+2 would have passed them. The behavioral signals - teleporting clicks, paste-only form fills, absent scroll movement - were the only reliable detector.
For detect-bot, the three-phase approach means different detection layers catch different automation tiers. Phase 1+2 catches tooling that patches browser attributes but can't hide from OS-level signals. Phase 3 catches AI agents that bypass fingerprint checks but can't fake continuous mouse movement. A browser that clears Phase 1+2 is a partially-patched automation tool - CloakBrowser, Camoufox, or invisible-playwright. A browser that clears Phase 3 is Playwright or an AI agent running on real Chrome. The combination narrows the evasion space.
[^1]: Ethan Wang, Zubair Shafiq, Yash Vekaria. "FP-Agent: Fingerprinting AI Web Agents Through Browser Automation Traces." arXiv:2605.01247, May 2026.