Inferensys

Glossary

Headless Browser Detection

A set of techniques that probe for the absence of a visible rendering surface or the presence of automation control flags in the JavaScript environment to identify browsers controlled by tools like Puppeteer or Playwright.
Developer demonstrating multi-agent tool use, agent tool selection interface on laptop, casual tech demo moment.
AUTOMATION FINGERPRINTING

What is Headless Browser Detection?

Headless browser detection encompasses the server-side and client-side techniques used to identify web clients operating without a visible graphical user interface, typically controlled programmatically via automation frameworks like Puppeteer, Playwright, or Selenium.

Headless browser detection is the practice of probing a visiting client's JavaScript runtime environment to distinguish a standard, user-operated browser from one executing in a 'headless' mode without a visible rendering surface. Detection scripts interrogate the presence of automation control flags—such as the navigator.webdriver property set to true—and evaluate the integrity of native browser APIs to identify modifications introduced by frameworks like Puppeteer or Playwright.

Advanced detection moves beyond static property checks to analyze behavioral inconsistencies, including the absence of human-like input timing, the execution speed of graphical rendering tasks, and discrepancies in WebGL fingerprinting that reveal the lack of a physical GPU. These signals are aggregated into a bot score by edge security platforms, enabling the real-time blocking or challenging of automated scraping traffic before it can extract proprietary content.

HEADLESS BROWSER DETECTION

Core Detection Techniques

Methods for probing the browser runtime to identify automation frameworks like Puppeteer, Playwright, and Selenium by detecting the absence of a visible rendering surface or the presence of automation control flags.

01

Navigator WebDriver Property

The navigator.webdriver property is the most fundamental leak point for automation detection. When a browser is controlled by Selenium, Puppeteer, or Playwright, this boolean is set to true by default. Detection scripts can simply evaluate navigator.webdriver === true to flag automated sessions.

  • Origin: Part of the W3C WebDriver specification, intended as a compliance signal for servers.
  • Evasion: Attackers often override this property using --disable-blink-features=AutomationControlled or by patching the getter with JavaScript proxies.
  • Counter-detection: Combine this check with other signals; never rely on this property alone.
W3C Standard
Specification Origin
Boolean
Data Type
02

CDP Runtime Detection

The Chrome DevTools Protocol (CDP) is the underlying communication layer that Puppeteer and Playwright use to control Chromium. Its presence can be detected by attempting to connect to the debugging port or by checking for CDP-specific artifacts in the runtime.

  • Debug Port: Check if http://localhost:9222/json is accessible from the browser context.
  • Runtime.evaluate: Detect if the Runtime domain has been injected by looking for modified Error.stack traces that include CDP-specific call frames.
  • WebSocket Connection: Monitor for active WebSocket connections to ws://127.0.0.1 with CDP-specific paths.
9222
Default Debug Port
03

Headless User-Agent Detection

Headless Chromium instances append HeadlessChrome to the default User-Agent string, creating an immediate and easily detectable signature. While this string can be overridden, many unsophisticated scrapers neglect to do so.

  • Default String: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/91.0.4472.77 Safari/537.36
  • Detection Logic: Parse the UA string for the substring HeadlessChrome.
  • Evasion: Attackers pass --user-agent argument with a legitimate browser string. Combine with TLS fingerprinting (JA4) to detect mismatches between the claimed UA and the actual TLS stack.
HeadlessChrome
Key Substring
04

Rendering Surface & Window Dimension Checks

Headless browsers lack a visible viewport, leading to detectable anomalies in screen dimensions and rendering behavior. These checks exploit the absence of a physical display.

  • Window Size: window.outerWidth and window.outerHeight often report 0 in default headless configurations.
  • Color Depth: screen.colorDepth may report default values inconsistent with real hardware.
  • WebGL Renderer: Querying the WebGL vendor and renderer string via WEBGL_debug_renderer_info often reveals SwiftShader or Google SwiftShader instead of a physical GPU name like ANGLE (NVIDIA GeForce RTX 4090).
SwiftShader
Headless GPU Renderer
05

Browser Feature & Permission Probing

Headless modes often disable or stub out features that require a physical user interface, creating detectable gaps in API behavior.

  • Permissions API: navigator.permissions.query({name: 'notifications'}) may return denied instantly without prompting, whereas a real browser would show a permission dialog.
  • Plugins Array: navigator.plugins.length is often 0 in headless mode, while real Chrome installations include internal plugins like Chrome PDF Plugin.
  • Touch Support: navigator.maxTouchPoints may report 0 even on platforms where a real browser would report a value, or the ontouchstart event handler may be absent from window.
0
Typical Plugin Count
06

JavaScript Engine Integrity Checks

Automation frameworks often modify native JavaScript functions to evade detection, but these modifications themselves leave artifacts. Integrity checks verify that core browser APIs remain unaltered.

  • Prototype Chain Inspection: Check if navigator.webdriver has been redefined using Object.getOwnPropertyDescriptor(Navigator.prototype, 'webdriver'). A custom getter indicates tampering.
  • Function toString: Evaluate Function.prototype.toString.call(window.chrome.runtime.connect). Native functions return function connect() { [native code] }. Automation tools that stub out APIs often fail to replicate this exact string.
  • Error Stack Traces: Throw a deliberate error and inspect the stack trace. Automation frameworks inject custom frames that are absent in native browser stacks.
[native code]
Expected toString Output
HEADLESS BROWSER DETECTION

Frequently Asked Questions

Technical answers to common questions about identifying and mitigating browser automation frameworks that simulate human interaction while lacking a visible rendering surface.

Headless browser detection is a set of techniques that probe for the absence of a visible rendering surface or the presence of automation control flags in the JavaScript environment to identify browsers controlled by tools like Puppeteer, Playwright, or Selenium. Detection works by interrogating the browser runtime for artifacts that differ between a standard user-facing browser and one running in headless mode. These artifacts include the navigator.webdriver property being set to true, missing or altered browser plugins, inconsistent rendering engine behaviors, and specific timing signatures in requestAnimationFrame callbacks. Modern detection stacks combine client-side JavaScript interrogation with server-side analysis of TLS fingerprints and HTTP/2 frame parameters to create a composite bot score that distinguishes automated sessions from genuine human traffic.

Prasad Kumkar

About the author

Prasad Kumkar

CEO & MD, Inference Systems

Prasad Kumkar is the CEO & MD of Inference Systems and writes about AI systems architecture, LLM infrastructure, model serving, evaluation, and production deployment. Over 5+ years, he has worked across computer vision models, L5 autonomous vehicle systems, and LLM research, with a focus on taking complex AI ideas into real-world engineering systems.

His work and writing cover AI systems, large language models, AI agents, multimodal systems, autonomous systems, inference optimization, RAG, evaluation, and production AI engineering.