Headless browser detected: causes and fixes (2026)
A site reports "headless browser detected", or it quietly serves a different page to your automated browser. Sites do this because a headless browser runs without a window and often differs from a normal desktop browser in small, measurable ways. This guide explains which signals sites look at, how to diagnose what your own browser is exposing, and what a sensible fix looks like. It also covers the point where the right answer is to stop, or to treat the check as a captcha step.
Why sites check for headless browsers
Automation frameworks drive a real browser engine, so most of the page works the same. The differences show up at the edges:
- Automation flags. Browsers driven by automation tools have historically exposed a
navigator.webdriverproperty set totrue, and some frameworks leave other traces in the page. - Missing or odd browser features. A headless build may report no plugins, an unusual list of languages, or a window size that does not match a real screen.
- Rendering differences. Canvas and WebGL output can differ between a headless and a headed environment, especially on a server without a GPU.
- Timing. Automated clicks and page loads often happen with a rhythm no person would produce.
- Network behaviour. The request headers, their order and the TLS handshake can betray a non-browser client.
Sites combine these signals into a score. A low score can mean a silent downgrade, a set of extra checks, or a block.
Step 1: see what your browser exposes
Before changing anything, look at what a page can read from your automation. The script below opens a page you control and prints the signals that matter. It is a diagnostic: use it on your own test pages, not to probe other people's systems.
from playwright.sync_api import sync_playwright
CHECK = """() => ({
webdriver: navigator.webdriver,
languages: navigator.languages,
plugins: navigator.plugins.length,
userAgent: navigator.userAgent,
screen: [screen.width, screen.height],
viewport: [window.innerWidth, window.innerHeight],
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone
})"""
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
page = browser.new_page()
page.goto("https://example.com") # replace with a page you own
print(page.evaluate(CHECK))
browser.close()
Run the same script with headless=False, then compare the two outputs. The differences are the things a site could notice.
Step 2: fix the inconsistencies, not just the flags
Hiding one flag while leaving others unchanged is the most common mistake. A user agent that says Windows while the fonts, timezone and screen size describe a Linux server is a stronger signal than a single property. Aim for an environment that is internally consistent:
- Use a headed browser where you can, or a headless mode that matches a current desktop browser.
- Set a viewport and a language that match each other.
- Keep the timezone and locale consistent with the IP address you use.
- Reuse one profile and its cookies for one identity, rather than starting fresh on every run.
Playwright lets you set several of these in one place:
context = browser.new_context(
locale="en-US",
timezone_id="America/New_York",
viewport={"width": 1366, "height": 768},
)
page = context.new_page()
Step 3: slow down and act like a visitor
Automation becomes obvious when it is faster than a person can be. Add realistic pauses between page loads, avoid clicking the same element in a perfect rhythm, and do not open hundreds of pages in parallel from one address. Doing fewer, slower requests also reduces the load on the site, which is the right thing to do anyway.
Step 4: know when the check is a captcha
Some detection flows end in a challenge rather than a block. You will see a captcha widget, and that step needs a token from a solver. The live captcha type at Scarnox today is PopularCaptcha. The PopularCaptcha Python guide shows how the token is requested and submitted. Other widgets, such as Cloudflare Turnstile, are on the roadmap but not yet live, so check captcha types explained before you plan around one.
A captcha step does not mean the rest of your setup is sound. If the same site blocks every headless session no matter how you configure it, that is a clear signal the owner does not want automated access. Respect it.
What not to do
Do not try to hide automation from a site that forbids it, and do not use detection-evasion tooling to get past access controls. That is a different kind of project from the testing and public-data collection this guide covers. If your project needs access you have not been granted, ask the site owner or look for an official API or data feed.
Scrape responsibly
- Check
robots.txtand the terms of the site before you automate it. - Keep request rates low, and cache pages you have already fetched.
- Treat a block as an answer, not as a puzzle to route around.
- Do not collect personal data without a lawful basis.
Where to go from here
If your automation is for end-to-end tests, the approach is different: see captcha in automated tests. If you are running a Playwright or Puppeteer setup, Playwright and Puppeteer captcha handling explains where the token goes.
Start with free trial credit, no card needed: create an account and test the PopularCaptcha flow on your own page. For current rates, see the pricing page.