Zum Hauptinhalt springen
Echtzeit-Radar & Feeds
Alle RSS Feeds ➔
👥 Community & Social
Windows Tipps & SecurityGrafikkarte vor Überhitzung schützen: So geht’s(25.09.2026 um 08:00 Uhr)
••••••••••
Windows Tipps & SecurityGrafikkarte vor Überhitzung schützen: So geht’s(25.09.2026 um 08:00 Uhr)
••••••••••
Intelligence View
⚡ tsecurity.de Intelligence

How Sites Detect Headless Browsers (And How to Evade Each Signal) — 2026 Guide

Websites can detect headless browsers through dozens of signals. Most scraper tutorials show you how to launch Playwright — none explain why it still gets blocked. Here's what sites are actually checking and how to evade each signal. …

0
↗ Quelle (dev.to)
Reagiere als Erste:r — dein Feedback zählt!

Websites can detect headless browsers through dozens of signals. Most scraper tutorials show you how to launch Playwright — none explain why it still gets blocked. Here's what sites are actually checking and how to evade each signal.






How headless detection works



Detection is probabilistic, not binary. Sites like Cloudflare, Akamai, and DataDome maintain a "bot score" based on many signals. Each anomaly adds to your score. Enough anomalies = block.



The most common signals:




  1. navigator.webdriver = true

  2. Missing browser plugins

  3. Specific headless Chrome flags

  4. Canvas/WebGL fingerprint anomalies

  5. Chrome DevTools Protocol (CDP) exposure

  6. User-agent / platform inconsistencies

  7. Missing media codecs

  8. Behavioral signals (mouse movement, timing)






Signal 1: navigator.webdriver



The most obvious signal. Chrome headless sets navigator.webdriver = true by default.



How to fix:




// Playwright
const context = await browser.newContext();
await context.addInitScript(() => {
Object.defineProperty(navigator, 'webdriver', {
get: () => undefined,
configurable: true,
});
});









# Playwright Python
async with async_playwright() as p:
browser = await p.chromium.launch(headless=True)
context = await browser.new_context()
await context.add_init_script("""
Object.defineProperty(navigator,
'webdriver', {
get: () => undefined
})
""")






Why this isn't enough: Sites also check for chrome.runtime, window.chrome, and CDP artifacts.






Signal 2: Chrome DevTools Protocol (CDP) detection



Playwright communicates with Chrome via CDP. Some sites detect open CDP connections:




// Detection technique used by some sites
const ws = window.WebSocket;
window.WebSocket = function(...args) {
if (args[0].includes('devtools')) {
throw new Error('DevTools detected!');
}
return new ws(...args);
};






Fix: Use --remote-debugging-port=0 to disable CDP discovery, or better — use camoufox which patches CDP exposure:




from camoufox.async_api import AsyncCamoufox

async with AsyncCamoufox(headless=True) as browser:
page = await browser.new_page()
await page.goto("https://bot-test.com")
# CDP not detectable from page JavaScript









Signal 3: Missing Chrome plugins and extensions



Real Chrome has plugins (navigator.plugins). Headless Chrome has zero:




// Detection: checks for plugins
if (navigator.plugins.length === 0) {
// Likely headless
}






Fix: Spoof plugins array:




// Add fake plugins
Object.defineProperty(navigator, 'plugins', {
get: () => [
{ name: 'Chrome PDF Plugin', filename: 'internal-pdf-viewer' },
{ name: 'Chrome PDF Viewer', filename: 'mhjfbmdgcfjbbpaeojofohoefgiehjai' },
{ name: 'Native Client', filename: 'internal-nacl-plugin' },
]
});









Signal 4: Canvas fingerprinting



Sites draw to canvas and compare the output hash. Headless Chrome produces a different hash than normal Chrome due to rendering differences.



Detection:




const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
ctx.fillText('fingerprint', 10, 50);
const hash = canvas.toDataURL();
// Compare hash to known-bot hashes






Fix: Use noise injection to randomize canvas output:




# Playwright: add canvas noise
await context.add_init_script("""
const originalFillText = CanvasRenderingContext2D.prototype.fillText;
CanvasRenderingContext2D.prototype.fillText = function(...args) {
const noise = Math.random() * 0.001;
this.globalAlpha = 1 - noise;
return originalFillText.apply(this, args);
};
""")






Or use playwright-stealth which handles this automatically:




from playwright_stealth import stealth_async

browser = await p.chromium.launch(headless=True)
page = await browser.new_page()
await stealth_async(page) # Patches all known detection vectors
await page.goto("https://bot.sannysoft.com")









Signal 5: Headless-specific user agent



Chrome headless used to include "HeadlessChrome" in the UA string. Now it doesn't, but other UA anomalies remain:




# BAD: Default headless UA has inconsistencies
browser = await p.chromium.launch(headless=True)

# GOOD: Use a real Chrome UA that matches the browser version
context = await browser.new_context(
user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36"
)






Also match the platform:




context = await browser.new_context(
user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64)...",
# navigator.platform must match the UA
extra_http_headers={"sec-ch-ua-platform": '"Windows"'}
)









Signal 6: Timing and behavioral signals



Bots move the mouse in straight lines, click instantly, and scroll at constant speeds. Sites use mouse heuristics to distinguish humans from bots.



Fix: Add realistic mouse movement:




import asyncio
import random

async def human_mouse_move(page, target_x, target_y, steps=10):
"""Move mouse in a curved path"""
box = await page.query_selector("body")
start = await page.evaluate("() => ({x: window.innerWidth/2, y: window.innerHeight/2})")

for i in range(steps):
# Bezier curve interpolation
t = i / steps
x = start['x'] + (target_x - start['x']) * t + random.gauss(0, 5)
y = start['y'] + (target_y - start['y']) * t + random.gauss(0, 5)

await page.mouse.move(x, y)
await asyncio.sleep(random.uniform(0.01, 0.05))

async def human_click(page, selector):
element = await page.query_selector(selector)
box = await element.bounding_box()

# Click slightly off-center (humans rarely click exact center)
x = box['x'] + box['width'] * random.uniform(0.3, 0.7)
y = box['y'] + box['height'] * random.uniform(0.3, 0.7)

await human_mouse_move(page, x, y)
await asyncio.sleep(random.uniform(0.1, 0.3)) # Pause before click
await page.mouse.click(x, y)









Signal 7: Chrome launch flags



Some --disable-* flags that make scraping easier also make you more detectable:




# Flags that increase detection risk:
args = [
"--disable-blink-features=AutomationControlled", # This is fine
"--disable-dev-shm-usage", # Low risk
"--no-sandbox", # INCREASES detection - only use in Docker
"--disable-setuid-sandbox", # Same
"--disable-web-security", # High detection risk - don't use
]

# Recommended minimal flags:
safe_args = [
"--disable-blink-features=AutomationControlled",
"--disable-infobars",
"--window-size=1280,800",
]









The complete stealth setup for Playwright Python






from playwright.async_api import async_playwright
import asyncio
import random

STEALTH_JS = """
// 1. Remove webdriver
Object.defineProperty(navigator,
'webdriver', { get: () => undefined });

// 2. Add Chrome object
window.chrome = { runtime: {} };

// 3. Add fake plugins
Object.defineProperty(navigator,
'plugins', {
get: () => [
{ name:
'Chrome PDF Plugin', filename: 'internal-pdf-viewer' },
{ name:
'Chromium PDF Viewer', filename: 'mhjfbmdgcfjbbpaeojofohoefgiehjai' },
]
});

// 4. Add fake languages
Object.defineProperty(navigator,
'languages', {
get: () => [
'en-US', 'en']
});

// 5. Fix permissions API
const originalQuery = window.navigator.permissions.query;
window.navigator.permissions.query = (parameters) => (
parameters.name ===
'notifications'
? Promise.resolve({ state: Notification.permission })
: originalQuery(parameters)
);
"""

async def create_stealth_browser():
p = await async_playwright().start()
browser = await p.chromium.launch(
headless=True,
args=[
"--disable-blink-features=AutomationControlled",
"--disable-infobars",
"--window-size=1366,768",
]
)
context = await browser.new_context(
user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36",
viewport={"width": 1366, "height": 768},
locale="en-US",
timezone_id="America/New_York",
extra_http_headers={
"Accept-Language": "en-US,en;q=0.9",
"sec-ch-ua": '"Chromium";v="122", "Not(A:Brand";v="24"',
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": '"Windows"',
}
)
await context.add_init_script(STEALTH_JS)
return context

# Test against bot detection
async def test_stealth():
context = await create_stealth_browser()
page = await context.new_page()
await page.goto("https://bot.sannysoft.com")
await page.screenshot(path="stealth-test.png")
print("Check stealth-test.png for results")

asyncio.run(test_stealth())









camoufox: the nuclear option



camoufox is a Firefox fork specifically built for stealth scraping. It modifies Firefox at the binary level to eliminate detection signals:




from camoufox.sync_api import Camoufox

with Camoufox(headless=True, os="windows") as browser:
page = browser.new_page()
page.goto("https://bot.sannysoft.com")
# Passes most detection tests






Install: pip install camoufox && python -m camoufox fetch



camoufox passes Cloudflare's bot checks in headless mode — something no Playwright configuration can do reliably.






What still doesn't work



Even with perfect browser fingerprinting, some sites detect bots via:





  • IP reputation: residential proxies required


  • Account behavior: new accounts with no history get restricted


  • JavaScript execution patterns: some sites analyze CPU timing


  • Network timing: consistent request intervals are a bot signal



For the hardest sites (LinkedIn, Google, major e-commerce), managed actors handle all of this:



Apify Scrapers Bundle ($29) — pre-built actors with stealth, proxy rotation, and behavioral randomization built in.






Quick reference: detection evasion checklist




  • [ ] navigator.webdriver = undefined

  • [ ] Add window.chrome.runtime

  • [ ] Spoof navigator.plugins (≥2 plugins)

  • [ ] Set realistic navigator.languages

  • [ ] Match UA string to actual Chrome version

  • [ ] Set timezone_id to match locale

  • [ ] Use --disable-blink-features=AutomationControlled

  • [ ] Avoid --no-sandbox in non-Docker environments

  • [ ] Add canvas noise injection

  • [ ] Use human-like mouse movement for click actions

  • [ ] Use residential proxies for IP reputation

  • [ ] Or use playwright-stealth / camoufox for automated patching








Pre-built actors for this use case:



1. Sofort-Triage & Abwehrmaßnahmen

SOC Incident Playbook: Remote Code Execution (RCE) Defense
Syntax validiert (0 Fehler)
title: Detect Exploitation - How Sites Detect Headless Browsers (And How to Evade Each Signal) — 2026 Guide
id: a643c24c-9085-4473-b40b-8644be55e7b9
status: experimental
description: Automatisch generierte SIEM-Erkennungsregel basierend auf CTI Intelligence
references:
  - https://tsecurity.de/
author: iShareStuff CTI Automated Detection Engine
date: 2026-09-26
logsource:
  category: network_connection
  product: any
detection:
  selection:
      DestinationIp:
        - '122.0.0.0'
  condition: selection
falsepositives:
  - Legitime administrative Zugriffe oder Penetrationstests
level: high
tags:
  - attack.initial_access
Syntax validiert (0 Fehler)
rule CTI_Threat_Indicator {
    meta:
        author = "iShareStuff CTI Automated Detection Engine"
        date = "2026-09-26"
        description = "YARA Signature for "
    strings:
        $str = "How Sites Detect Headless Brow" ascii wide
    condition:
        any of them
}
Syntax validiert (0 Fehler)
index=security sourcetype IN ("cisco:asa", "pan:traffic", "zeek_conn", "suricata", "WinEventLog:Security")
(dest_ip="122.0.0.0")
| stats count earliest(_time) as first_seen latest(_time) as last_seen by src_ip, dest_ip, dest_host, signature
| eval first_seen=strftime(first_seen, "%Y-%m-%d %H:%M:%S"), last_seen=strftime(last_seen, "%Y-%m-%d %H:%M:%S")
| sort - count
Syntax validiert (0 Fehler)
destination.ip: ("122.0.0.0") and event.category: "network"
Syntax validiert (0 Fehler)
CommonSecurityLog
| where DestinationIP in ("122.0.0.0")
| summarize EventCount = count(), FirstSeen = min(TimeGenerated), LastSeen = max(TimeGenerated) by SourceIP, DestinationIP, DestinationPort, Activity
| extend DetectionRule = "iShareStuff-CTI-Compiled"
| sort by EventCount desc

2. Cyber Threat Intelligence & Forensik

IoC Intelligence (1 Indikatoren)
122[.]0[.]0[.]0
CTI Threat Relationship Graph4 Knoten / 3 Relationen
CVE / Incident Software MITRE ATT&CK CWE Weakness IoC
🎯
MITRE ATT&CK Matrix Navigator 14 Taktiken
Reconnaissance
-
Resource Development
-
Initial Access
Execution
Persistence
-
Privilege Escalation
Defense Evasion
Credential Access
-
Discovery
-
Lateral Movement
-
Collection
-
Command and Control
Exfiltration
-
Impact
tsecurity.de Cognitive Threat RAG
Fokus-Vektor:

Kognitive Analyse für identifizierte Bedrohung: Erhöhte Bedrohungslage im Bereich How Sites Detect Headless Browsers (And .... Basierend auf 368k Vektor-Korrelationen werden sofortige Isolationsmaßnahmen für betroffene Endpunkte empfohlen.

🛡️ Angriffsfläche & Exposure

Netzwerk/Remote-Zugriff ohne Vorauthentifizierung möglich.

⚡ Empfohlene Sofortmaßnahmen
  • 1. Perimeter-Inspektion: Relevante Portfreigaben und exponierte Endpunkte unverzüglich scannen.
  • 2. Patch-Applikation: Hersteller-Hotfix einspielen oder betroffene Daemons in isolierte DMZ-Segmente überführen.
  • 3. Telemetrie & EDR-Alerts: Prozessaufrufe und Child-Processes auf anomale Shell-Spawns überwachen.
🔗 Semantisch verwandte Zero-Days MariaDB 11.7 VEC
Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten How Sites Detect Headless Browsers (And How to Evade Each Signal) — 2026 Guide

Thematisch verwandte Begriffe: Sites, Detect, Headless, Browsers · 6 Treffer

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Zum Aktualisieren ziehen
ZERO-DAY CVE-2026-100534 | OpenClaw versions before 2026.8.1 contain an authorization bypass vulne…
Advisory →
tsecurity.de Icon
Offline-Lesen, Eilmeldungen & 0ms Ladezeit

Installiere tsecurity.de direkt auf deinen Home-Bildschirm für das ultimative Vollbild-Magazinerlebnis ohne Browser-Leisten.

Nächster Beitrag