No single magic header detects a stealth browser. You catch the small inconsistencies that automation and fingerprint-spoofing tooling leave behind, then confirm them with behavior over the session. A modern stealth setup patches the obvious tells (navigator.webdriver, the missing window.chrome object, headless rendering quirks), so detection has to read the layers those patches can't fully cover: the automation control surface, the internal coherence of the fingerprint, and how the session behaves once it is on the page.
This is the operator's how-to. Below are the concrete signal classes worth instrumenting, what each one catches, and what a well-configured stealth browser defeats. cside collects these from inside the browser runtime, so the goal here is to make the signals legible. If you want the conceptual background on what stealth and anti-detect browsers are, read the stealth browsers explainer first; this post assumes you already want to find them.
Which signals actually reveal a stealth browser?
No single class is sufficient. Strong detection stacks all four and weights them by how hard each is to fake.
| Signal class | What it catches | What good stealth tooling defeats |
|---|---|---|
| Automation / CDP tells | Unpatched headless and DevTools-driven sessions | navigator.webdriver override, injected window.chrome |
| Headless rendering quirks | Vanilla headless Chrome | Headful Chrome driven via CDP, virtual display servers |
| Fingerprint inconsistency | Spoofed devices with internal mismatches | Well-built anti-detect profiles that stay self-consistent |
| Behavioral signals | Machine timing, input entropy, navigation rhythm | Slowed, jittered, human-in-the-loop scripted sessions |
Automation and CDP tells
The cheapest tells come from the automation control surface itself. Plain Playwright or Selenium-driven Chrome sets navigator.webdriver to true, ships a missing or hollow window.chrome object, and exposes permissions.query behavior that disagrees with what a real Chrome returns (for example, notification permission reading denied while Notification.permission says default). Each of these is a one-line read.
Stealth libraries exist specifically to erase these. playwright-stealth and its peers override navigator.webdriver, inject a realistic window.chrome, normalize navigator.plugins, and fix the permissions mismatch, which is why blocking Playwright automation takes more than reading a single flag. cside's 2026 web security research reports that playwright-stealth installs rose roughly 10x through 2025, a useful proxy for how fast this evasion moved into mainstream tooling (cside 2026 research).
The patched flags become confirming evidence rather than primary evidence. The harder tell is the Chrome DevTools Protocol binding underneath the automation. CDP-driven sessions can leak through side effects: the runtime's behavior under specific Runtime/Console interactions, serialization quirks in error stacks, or timing differences when the protocol is attached. Those are far more expensive for an operator to fully suppress than a single boolean, because they emerge from how the browser is being controlled, not from a property they can rewrite.
Headless rendering and environment quirks
Vanilla headless Chrome still betrays itself through the rendering and device environment. The classics: a user-agent string containing HeadlessChrome, zero installed plugins where a real desktop Chrome reports a small set, a missing or fixed-size outer window, no battery or media-device APIs populated, and subtle differences in how fonts and images rasterize without a real display.
In 2026, serious operators have moved past headless mode. An AI agent can drive a full, headful Chrome through CDP on a virtual display, which removes every headless-specific tell while keeping the automation underneath. Headless detection catches the lazy tier and forces everyone else up a level, where the fingerprint and behavior layers take over. This is also where legacy bot-detection tools miss AI agents: they were built to flag headless signatures, not the control surface beneath a headful browser.
Fingerprint inconsistencies: the cracks in a spoofed device
Anti-detect browsers replace fingerprint-generating APIs with synthetic values so each session looks like a fresh device. The detectable failure mode is internal incoherence: the spoofed values don't agree with each other. Lower-quality profiles are where the cracks show.
- Font list vs. claimed OS. A profile claiming macOS that enumerates Windows-only fonts (or omits the fonts every Mac ships) is spoofed. The font set has to match the platform it pretends to be.
- WebGL renderer vs. plausible hardware. A
WEBGL_debug_renderer_infostring that names a GPU no shipping device pairs with the claimed OS and screen profile is a tell. Spoofers pull from a library of real strings, but pairing them coherently with everything else is hard. - Canvas and AudioContext output. Anti-detect browsers return deterministic synthetic canvas pixels and audio output to defeat hashing. Output that is statistically implausible, too uniform, or identical across "different" sessions flags the synthesis.
- Hardware concurrency, memory, and screen geometry.
navigator.hardwareConcurrency,deviceMemory, and screen/viewport dimensions that contradict the claimed device class (a phone UA reporting a 32-core desktop) break coherence.
A well-built anti-detect profile keeps all of these consistent with each other, which is exactly why fingerprinting alone can't be the whole answer. It raises the cost and catches the sloppy tier; behavior closes the rest.
Behavioral signals: what spoofing can't suppress
The fingerprint layer describes the device. The behavioral layer describes the operator, and that is much harder to fake because it has to be produced live, every session. Humans interact imprecisely: mouse paths curve and overshoot, scroll speed varies, form fills include pauses, typos, and corrections, and there is a measurable gap between page-ready and first interaction.
Automated sessions tend toward the opposite: instant form fills with no correction, navigation with no hesitation, scrolling at fixed intervals, and event timing with near-zero jitter. None of that is suppressed by a navigator.webdriver override or a clean canvas hash, because it comes from how the agent acts, not from what it claims to be. Cross-session correlation finishes the job. Many sessions inside a short window that share structural fingerprint traits, behavioral timing, and post-session actions reveal coordination that no single session would expose. For how agentic tooling specifically tries to mimic human rhythm, see how OpenClaw agents bypass bot detection.
How to turn signals into a decision
Detection is only useful if it drives a graduated response. Don't block on one signal; weight several and act on convergence.
- Instrument all four signal classes at runtime, inside the browser session.
- Compare the claimed environment (user-agent, platform, device class) against the observed one and record every mismatch.
- Add network context (datacenter vs. residential IP, ASN, proxy and geo-shift behavior) as a weight, never as a sole verdict.
- Score the session on converging independent signals, and keep the raw signal set, risk reason, and challenge outcome as evidence.
- Block high-confidence sessions at the points that matter most, such as registration, where browser-layer detection catches fake account creation, and checkout. Challenge medium-confidence ones with step-up friction, and allow-with-monitoring for legitimate agent traffic.
cside's AI Agent Detection runs this from inside the page: it reads automation and CDP tells, captures device and real-IP signals, surfaces fingerprint inconsistencies, and watches behavior over the session, then lets teams allow known-good automation, challenge suspicious sessions, and block high-risk agents with a full audit trail.







