Headless Browser Detection: How Sites Identify Puppeteer, Playwright, and Selenium

Headless browser detection combines WebDriver, DevTools Protocol, fingerprint, and behavioral signals to distinguish automated sessions from regular browsing.
Headless mode is useful for QA, regression testing, accessibility testing, and approved internal workflows. However, a browser without a visible window often starts with a different state, timing profile, and set of capabilities. Automated browsing detection rarely depends on a single flag: a modern WAF evaluates a body of evidence along with the session's reputation.
Puppeteer, Playwright, and Selenium are not bots by themselves. They are browser control tools. Problems arise when a system tries to pass a test environment off as a regular visitor or violates a site's rules. For legitimate automation, the better approach is a consistent profile, transparent authorization, and an official API.
What is a headless browser and when should you use one?
A headless browser runs a browser engine without a visible window. It builds the DOM, executes JavaScript, and loads CSS and network resources, making it suitable for end-to-end testing and rendering. Headless browsing does not mean graphics are absent: modern Chrome can use a real rendering pipeline, though the launch method still leaves contextual signals.
Puppeteer works closely with Chromium through the Chrome DevTools Protocol. Playwright can control several browser engines and create isolated browser contexts. Selenium implements the WebDriver standard and controls a browser through a driver. Their APIs and architectures differ, but a security service sees the outcome: client properties, event sequences, network traces, and actions within the session.
For your own QA system, choose headless mode for speed and reproducibility, and headed mode when you need to debug. Do not assume that headed mode automatically looks human. It is simply another execution mode.
Which Puppeteer, Playwright, and Selenium markers do sites check?
The best-known marker is navigator.webdriver. This standard property can tell a page that WebDriver controls the browser. It is useful in your own tests, but it is not enough for antifraud decisions: its absence proves nothing, while its presence can also occur in corporate QA environments.

Other markers appear at launch: process arguments, the automation extension, driver environment variables, empty window properties, specific global objects, or unusual permission states. A headless environment may have a 0×0 window, an unnatural outerWidth, an empty plugin list, no PDF viewer, or a limited set of media codecs. These are not universal rules. Browser versions change quickly.
// Diagnostics for your own test page, not an evasion mechanism
const report = {
webdriver: navigator.webdriver,
viewport: [window.innerWidth, window.innerHeight],
outerWindow: [window.outerWidth, window.outerHeight],
plugins: navigator.plugins.length,
language: navigator.language
};
console.table(report);
Consistency matters more. For example, the User-Agent may claim a current desktop Chrome version while navigator.plugins, screen, timezone, WebGL, Client Hints, and media capabilities describe a minimal container. One mismatch is not alarming. Five independent mismatches create a strong classification signal.
How do CDP and timing leaks expose instrumentation?
Chrome DevTools Protocol gives automation powerful access to script execution, network interception, DOM inspection, and device emulation. At the same time, instrumentation can sometimes alter event ordering, call stacks, error formats, or object serialization timing in console-like APIs. Some detectors deliberately create a getter or an Error and observe whether the object receives unusual interaction.
A timing leak is not a magic test. Execution depends on the CPU, system load, garbage collection, network, and the container scheduler. A capable WAF gathers many measurements and evaluates their distribution instead of blocking a session because of one delay. For developers, this means random sleep calls are the wrong fix. They make a test brittle and do not reproduce real interaction.

The diagram shows that a WAF reaches a decision after correlating several independent layers, not from a single JavaScript property.
Why do WAFs evaluate fingerprints, networks, and behavior together?
Cloudflare Turnstile, DataDome, Akamai, and similar solutions operate as risk-scoring systems rather than checks for one JavaScript property. They may compare fingerprint stability, TLS and HTTP signals, IP reputation, cookie history, navigation speed, pointer events, window focus, and challenge responses. The exact rules are private and change over time, so claims about one mandatory signal quickly become outdated.
The behavioral layer is especially important. Physical input generates uneven streams of pointer, wheel, and keyboard events. Automation may produce perfectly regular intervals, instant form completion, identical pauses, or none of the usual blur and focus transitions. Conversely, a perfectly drawn mouse path does not make a session legitimate. Antifraud systems see the wider context.
| Inspection layer | Example signals | Why one signal is not enough |
|---|---|---|
| browser | WebDriver, plugins, codecs, screen | operating system versions and policies differ |
| CDP and runtime | global objects, errors, event order | tools and browsers continue to evolve |
| network | IP, TLS, HTTP headers, location | legitimate users connect through VPNs and offices |
| behavior | pace, focus, pointer activity, navigation | accessibility tools and QA can look unusual |
How do you build a controlled test environment for headless detection?
A controlled environment helps separate a genuine automation artifact from an incidental environmental difference. One run on a developer's laptop proves nothing. A minimum matrix should cover headed and headless modes, clean and reused profiles, the current and previous Chromium versions, and at least two network routes with known reputations. Run every combination several times with the same scenario.
Divide the environment into three parts. A control page collects permitted browser and runtime signals. An orchestrator launches the browser and records its configuration. A results store connects measurements with the engine version, launch time, and test ID. Raw cookies, tokens, and passwords must stay out of the dataset.
Which run matrix should you use?
Change only one factor at a time. First, compare headed and headless modes for the same binary, profile, and IP. Next, change the browser version without touching the network. Test a clean profile separately from a warmed profile that already has cookie and cache history. This design reveals which factor caused the difference.
| Factor | Control value | Variant | What to measure |
|---|---|---|---|
| mode | headed | headless | runtime and rendering differences |
| profile | clean | reused | storage, permissions, history |
| browser | current | previous | regressions after an update |
| network | allowlisted IP | corporate route | IP, TLS, and latency |
| input | manual baseline | test script | event order and timing |
Statistics should include more than averages. Record the median, interquartile range, and failed-run rate. Timing signals have heavy tails because of garbage collection and scheduling, so one slow call cannot serve as proof.
Which CDP and Runtime telemetry should you collect?
For your own control page, it is enough to log lifecycle event order, execution context creation, JavaScript errors, the availability of key APIs, and the intervals between navigation, DOMContentLoaded, and completion of the test action. You do not need to collect private page content. The goal is reproducibility, not user surveillance.
Separate CDP telemetry from signals available to ordinary JavaScript. If a detector knows something only because the test harness opened a DevTools session, that is a lab artifact. It may not exist for a production visitor. In the report, label the source of every field: page API, browser log, network capture, or orchestrator.
A practical record for one run may include test_id, Chromium build, launch mode, profile age, viewport, locale, proxy route, timestamps, and an error list. Redact secrets before storage. Computing a diff between two runs is convenient, but you still need the original values to explain the cause.
Three CDP domains provide the most useful signals for your own test environment. Page records frameStartedLoading and navigatedWithinDocument, Runtime exposes executionContextCreated and script execution order, while Network provides responseReceived and loadingFinished with precise timestamps. When these events occur in an unusual order relative to DOMContentLoaded, the cause is more likely external instrumentation than slow rendering on an underpowered CI runner. For a control page, logging these three domains as one JSON line per event and matching the resulting timeline with the final test result, such as allow, challenge, or block, is enough. This log does not require access to private page content, but it still shows whether the discrepancy came from CDP instrumentation or ordinary network latency. Retain aggregated metrics across several runs instead of a raw log for every session so the environment remains easy to audit.
How should you test events and timing leaks?
Synthetic interaction differs in more than its mouse path. Examine the order of pointerdown, mousedown, focus, input, change, and click, along with the isTrusted property. Do not turn these fields into rigid rules: assistive technologies, remote desktops, and corporate RPA can legitimately generate unusual sequences.
For timing analysis, repeat one test dozens of times and compare distributions. Random pauses do not create human behavior; they only add noise. It is more useful to measure causal relationships: does the scenario wait for an element to appear, does it perform the next action before layout completes, and does it respond consistently to a slow network?
How should you interpret false positives and WAF changes?
A false positive occurs when an approved session receives a challenge or block because it resembles automation. Disabling a rule globally is not the right fix. Identify the layer that contributed most, test it against a control group, and adjust its weight or condition only where evidence supports the change.
Which metrics show detector quality?
Precision answers what proportion of flagged sessions truly belong to unwanted automation. Recall shows what proportion of such sessions the system finds. The false positive rate among ordinary users and the challenge completion rate for legitimate visitors matter just as much to a business. A single overall accuracy score can conceal problems when the classes are imbalanced.
Measure these indicators separately for desktop and mobile users, browser versions, accessibility scenarios, corporate networks, and test robots. After a Chromium release or WAF policy change, compare them with the previous baseline. A sudden increase in challenges for one segment often indicates incompatibility rather than a sudden wave of attacks.
How should you document a decision for audit purposes?
A decision log should include the time, ruleset version, pseudonymized session ID, contribution from each layer, and final action: allow, challenge, or review. Do not retain full behavioral traces longer than needed for an investigation. Restrict access by role, and require code review or formal approval for threshold changes.
Explainability also matters to the support team. The statement “blocked by the model” is not actionable. A useful response identifies network reputation or a browser capability conflict and suggests a safe verification path. This process reduces pressure on the development team without disclosing exact security thresholds.
How do you build legitimate automation without brittle stealth patches?
Site owners need an allowlist for test accounts, a separate staging domain, test credentials, and an observable User-Agent. Teams integrating with another service need written permission, rate limits, an official API, and a contact for false positives. Hiding WebDriver through page patches does not resolve conflicts between APIs and may violate the platform's terms.
Test profile consistency instead. In Afina, each account uses an isolated Chromium profile with its own fingerprint, proxy, cookies, and cache, while visual scenarios can run approved business processes. Automation through the local API lets you control launches and keep logs without covertly interfering with another site's challenge flows.
- identify the system owner, permission, and allowed request frequency
- create a separate profile and test account for the specific workflow
- verify the UA, timezone, screen, language, and media capabilities on an internal control page
- log WAF errors and coordinate them with the resource owner instead of bypassing a challenge
- keep Chromium and automation libraries current
Technical discipline is more useful here than stealth plugins. They typically fix one known symptom while leaving other channels inconsistent.
What is the difference between headless, headed, and antidetect profiles?
Headless is a launch mode. Headed is the same browser with a visible window. An antidetect profile is a separate environment with isolated data and controlled parameter consistency. These concepts overlap, but they do not replace one another.
| Option | Strength | Typical limitation |
|---|---|---|
| headless | fast CI and regression tests | noticeable environmental differences |
| headed | convenient debugging and visual review | does not resolve fingerprint conflicts by itself |
| isolated profile | separates cookies, cache, proxy, and settings | requires a usage policy and access controls |
How should you investigate a WAF false positive?
When a legitimate test receives a challenge or denial, start with evidence instead of trying to change dozens of browser flags. Save the request ID, UTC time, browser version, launch mode, network route, response code, and a minimal HAR or network log without secrets. Then reproduce the action in staging or with a test account. This tells you whether the block came from IP reputation, a fingerprint problem, or a business rule.
Build a run matrix with one browser in headed and headless modes, one approved IP and one ordinary corporate route, and the current and previous library versions. Change one factor at a time. Otherwise, the team gets plenty of noise and no cause. Compare not only whether a block occurred but also when: before the page loaded, after JavaScript ran, during login, or after a specific action.
The WAF owner can add a dedicated test segment, service account, or rule for a signed webhook. This is considerably safer than turning a QA scenario into a race against private heuristics. Keep accessibility in mind as well: screen readers, back-office RPA, and corporate VDI infrastructure can behave unusually without being malicious.
What should a safe automation runbook include?
A runbook starts with its purpose and boundaries: which resource the company owns, what data may be read, who approves a run, and how to stop the scenario. Add a rate limit, idempotency, restart behavior, an event log, and a way to revoke the token. Automation that cannot stop can turn a minor error into an incident.
For the browser step, separately document the profile, local storage, proxy, Chromium version, and authentication method. Do not put passwords in code or HAR files. If many operators need the workflow, use roles and isolated test accounts. This approach makes audits easier and reduces the chance that the WAF sees a chaotic request stream.
Why must risk scoring be explainable?
An antibot system without a reason log is hard to maintain. The resource owner needs to know whether WebDriver availability, a parameter conflict, network reputation, or an unusual action rate influenced the decision. This makes it possible to fix the test route without weakening protection for everyone. A model that returns only “bot” pushes teams toward unsafe experimentation instead of proper integration. An explainable result also makes it easier to verify changes after a Chrome update or WAF policy release. The team can tie a risk threshold to a rule and safely roll back the release if false blocks increase. Review metrics separately for users, test robots, and assistive technologies so an average does not conceal a problem. This material is provided solely for informational and educational purposes.
For QA, this means clearly separating production visitors from tests. For privacy, it means keeping sessions separate and storing profile data locally. Assign someone to approve runs, retain logs, and promptly disable a scenario after an incident. Record the driver version, timezone, window size, and network request route separately. Afina supports the isolation of these profiles and scenarios, but it does not guarantee passage through any site's security controls.
DownloadFAQ — Frequently Asked Questions
What is a headless browser?
A headless browser runs a browser engine without a visible window. It is used for testing, rendering, and approved automation.
Does navigator.webdriver always mean a bot?
No, it indicates WebDriver automation, not malicious intent. QA and accessibility tests may expose it as well.
Why do sites detect Playwright or Puppeteer?
Sites analyze a combination of runtime, fingerprint, network, and behavioral signals, not just the library. One marker is rarely decisive.
What is a CDP leak?
A CDP leak is a side effect of controlling a browser through the Chrome DevTools Protocol. It may appear in event order, call stacks, or object handling.
Does headed mode help pass a WAF?
Headed mode removes only some differences associated with headless execution. It does not guarantee that the fingerprint, network, or behavior meets the site's policy.
How can you legally test a WAF with browser automation?
Use staging, test accounts, an allowlist, and written authorization from the owner. Log detections and tune the rules with the security team.
Can you remove every sign of automation from a headless browser?
No, you cannot remove every signal because a WAF evaluates runtime, network, and behavioral evidence together. Patching one parameter addresses only one of many independent markers.
What should you do if a WAF blocks a legitimate QA test?
Collect the request ID, time, browser version, and network route, then reproduce the action in staging with a test account. Ask the resource owner for allowlisting instead of trying to bypass the challenge.
How is a CDP leak different from a timing leak?
A CDP leak results from the DevTools Protocol connection and changes event order or error stacks. A timing leak is a statistical difference in execution speed evaluated across many measurements.
