Skip to content
Cascading Labs QScrape VoidCrawl Yosoi

Example: Stealth Mode

See what stealth mode changes by comparing fingerprint signals with stealth on and off.

Code

import asyncio
import json
from voidcrawl import BrowserConfig, BrowserSession, Page
DETECTION_JS = """
JSON.stringify({
webdriver: navigator.webdriver,
plugins_count: navigator.plugins.length,
languages: navigator.languages,
has_chrome_runtime: typeof window.chrome !== 'undefined'
&& typeof window.chrome.runtime !== 'undefined',
})
"""
async def check_fingerprint(label: str, page: Page) -> None:
raw = await page.evaluate_js(DETECTION_JS)
fingerprint = json.loads(raw)
print(f"\n[{label}]")
for key, value in fingerprint.items():
print(f" {key}: {value}")
async def main() -> None:
# Stealth ON (default)
async with BrowserSession(BrowserConfig(stealth=True)) as browser:
page = await browser.new_page("https://qscrape.dev")
await check_fingerprint("stealth=True", page)
await page.close()
# Stealth OFF
async with BrowserSession(BrowserConfig(stealth=False)) as browser:
page = await browser.new_page("https://qscrape.dev")
await check_fingerprint("stealth=False", page)
await page.close()
if __name__ == "__main__":
asyncio.run(main())

What to Look For

SignalStealth ONStealth OFF
navigator.webdriverundefined or NoneTrue
plugins_count> 0 (real plugins)> 0 (real plugins)
languagesRealistic listRealistic list
has_chrome_runtimeTrueTrue

The key difference is navigator.webdriver. With stealth on, it’s removed from the prototype chain. With stealth off, Chrome sets it to true, which is the primary signal WAFs check.

See the Stealth Mode guide for the full explanation of what’s patched and why.