Browser Anti-Detection (Part 2): GPU Pipelines, Keystroke Entropy, and Client Hints
Input event entropy, GPU rendering pipeline differences, Client Hints headers, CDP Runtime detection — sites still checking navigator.webdriver in 2026 are years behind.
Do Classic Detection Signals Still Work in 2026?
Direct answer: Individual signals are mostly dead. Combined signals are what matter.
navigator.webdriver — defeated in 2018 by puppeteer-extra-plugin-stealth.
window.chrome missing — patched by stealth plugins in 2020.
Plugin array empty — patched in 2019.
WebGL VENDOR/RENDERER — stealth plugins override getParameter return values.
User-Agent — replaced by the Client Hints system.
Every static property check has a stealth plugin counter. Detection in 2026 has moved to signals that cannot be bypassed by simple property overrides.
Signal 1: Input Event Entropy
This is the most reliable single signal. Real human input — even very fast input — produces event streams with predictable physical characteristics.
Mouse movement: Human mouse movement isn't straight. Each movement has microscopic jitter, acceleration curves follow Fitts' law. CDP-driven mouse movement, even with stealth plugins, produces synthesized events:
- CDP synthesized: integer-pixel coordinates, perfectly straight or curved trajectories, identical inter-event timing
- Real human: sub-pixel coordinates, trajectory jitter, velocity varying by distance
Keyboard input: Real typing has key dwell times varying between 30-120ms. Different keys have different dwell times (space is usually shorter than letters). CDP's keyDown/keyUp events either have perfectly uniform timing or are too random — lacking the natural distribution of human typing.
The specific detection rule: a session performing meaningful actions (login, checkout, payment) without preceding mouse movement is 99% bot.
Detection logic (pseudocode):
if session has meaningful_action (login/checkout/submit):
if mouseMoveCount == 0:
return "bot - no mouse movement before action"
if mouseMoveEntropy < threshold:
return "bot - insufficient movement entropy"
if keyDwellStdDev < threshold:
return "bot - keystroke timing too uniform"Signal 2: GPU Rendering Pipeline
Headless Chrome's GPU pipeline differs from headful Chrome's, even with --use-gl=swiftshader or hardware acceleration emulation.
Specific differences:
Canvas subpixel text positioning. The same text rendered headless vs. headful produces systematically different subpixel positions. Stable and predictable. Detection systems maintain a hash table of "known Canvas rendering values → CDP-driven Chrome."
Bezier curve aliasing patterns. Canvas path rendering produces characteristic aliasing differences between headless and headful modes.
WebGL readPixels output. Reading pixels from a controlled scene produces different byte-level output between modes.
Stealth plugins don't touch the GPU pipeline because modifying GPU output requires swapping the renderer — which itself produces detectable inconsistencies.
Signal 3: Client Hints
Chrome's User-Agent Reduction project has fundamentally changed how browsers identify themselves. The old UA string Mozilla/5.0 (Windows NT 10.0; ...) has lost most of its information value. Chrome now sends minimal default Client Hints headers:
Sec-CH-UA: "Google Chrome";v="124", "Chromium";v="124"
Sec-CH-UA-Mobile: ?0
Sec-CH-UA-Platform: "Windows"If the server wants more, it sends Accept-CH in its response. Chrome decides whether to comply:
Accept-CH: Sec-CH-UA-Arch, Sec-CH-UA-Full-Version-List, Sec-CH-UA-ModelFor detection systems:
- Old UA spoofing is insufficient — Client Hints must match the claimed browser version
- If Sec-CH-UA claims Chrome 124 but browser behavior matches Chrome 130, inconsistency is detected
- High-entropy values (platform, architecture, device model) must align with other fingerprint signals
Signal 4: CDP Runtime Detection
Even with all stealth patches applied, Chrome DevTools Protocol leaves a small but measurable runtime signature.
The technique: call console.debug with a %c-formatted argument inside a getter that's accessed during DevTools attachment. This produces a ~0.3ms measurable timing delta when DevTools is attached — which CDP fundamentally is.
This signal needs re-validation every few months — Chromium's CDP team patches known detection techniques. As of mid-2026, multiple variants still work.
Scoring, Not Blocking, Is The Key
Individual signals all have false positives:
- Touch device users have zero mouse events
- Some GPUs behave abnormally in headless mode
- VPN users may trigger suspicious patterns
The correct approach is score-based gating:
score = calculate_automation_score(input_entropy, gpu_pipeline, client_hints, cdp_signal)
if score < 0.3:
action = "allow"
elif score < 0.6:
action = "step_up" # CAPTCHA or email confirmation
elif score < 0.9:
action = "hard_challenge" # honeypot or 2FA
else:
action = "block"What This Means for Automation
If you maintain browser automation pipelines, the 2026 reality is:
Transport layer fingerprints (Part 1) and application-layer behavioral signals (this article) combine to create a detection surface that's harder to bypass than ever. Stealth plugins alone aren't enough. C++ engine-level anti-detection alone isn't enough. What's needed is consistency across transport, application, and behavioral layers.
The next article analyzes why consistency matters most: why long-lived stable environments survive longer than aggressive rotation.
Need an enterprise proxy plan?
We can tailor architecture to your target domains, concurrency, and reliability goals.