Most AI browser agents fail in production because websites detect and block them. Here is what triggers detection and what actually works.
The Problem
Browser Use lets AI agents control a real Chromium browser -- clicking, typing, navigating, and extracting data just like a human would. The gap that most tutorials skip: modern websites detect headless browsers and bot traffic, often blocking or serving degraded content to them before your agent has a chance to do anything useful.
Even on the WebArena benchmark, state-of-the-art browser agents succeed on only about 35% of real-world web tasks. A significant portion of those failures are detection-related. This article covers the detection mechanisms, which ones Browser Use handles by default, and what you need to configure for the sites that don't cooperate.
How Websites Detect Headless Browsers
| Detection method | What it checks | Browser Use default handling |
|---|---|---|
| User-Agent string | Headless Chromium has a distinct UA string | Partially -- configure explicitly |
| navigator.webdriver | JS property set to true in automation contexts | Handled by Playwright stealth |
| Missing browser fingerprint | Plugins, screen resolution, hardware concurrency | Partial -- needs configuration |
| Mouse movement patterns | Bots move in straight lines, humans don't | Not handled -- you add this |
| Request timing | Bots request pages too fast | Not handled -- add delays |
| CAPTCHA / Cloudflare | Active challenge pages | Not handled -- needs service |
Step 1: Configure a Realistic User Agent
from browser_use import Agent
from browser_use.browser.browser import Browser, BrowserConfig
from langchain_anthropic import ChatAnthropic
# Use a real, up-to-date Chrome user agent string
browser = Browser(
config=BrowserConfig(
# Match the Chrome version on your system for consistency
extra_chromium_args=[
"--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/131.0.0.0 Safari/537.36",
],
)
)
agent = Agent(
task="Find the latest pricing for the Pro plan",
llm=ChatAnthropic(model="claude-sonnet-4-6"),
browser=browser,
)
await agent.run()Step 2: Enable Playwright Stealth Mode
playwright-stealth patches the most common JavaScript-based detection mechanisms: navigator.webdriver, missing plugins, incorrect Chrome runtime properties, and others. Install and apply it to your Browser Use browser context.
pip install playwright-stealthfrom browser_use import Agent
from browser_use.browser.context import BrowserContext, BrowserContextConfig
from playwright_stealth import stealth_async
# Override the browser context to apply stealth on page creation
class StealthBrowserContext(BrowserContext):
async def _create_page(self):
page = await super()._create_page()
await stealth_async(page) # apply stealth patches
return page
agent = Agent(
task="Extract product data from the catalogue",
llm=ChatAnthropic(model="claude-sonnet-4-6"),
browser_context=StealthBrowserContext(
config=BrowserContextConfig(
viewport={"width": 1366, "height": 768}, # common screen size
)
),
)Step 3: Add Human-Like Timing
Bots access pages at machine speed -- instantly. Humans pause, scroll, and take time between actions. Adding small randomised delays between agent actions significantly reduces detection on timing-sensitive sites.
import asyncio
import random
from browser_use import Agent, Controller
class SlowController(Controller):
async def act(self, action, browser_context):
# Random human-like pause before each action (0.5 - 2.5 seconds)
await asyncio.sleep(random.uniform(0.5, 2.5))
return await super().act(action, browser_context)
agent = Agent(
task="Log in and check account balance",
llm=ChatAnthropic(model="claude-sonnet-4-6"),
controller=SlowController(),
)For sites that do timing analysis, combine random delays with occasional longer pauses (3-8 seconds) to simulate reading or thinking time. Pure random is better than fixed delays, which can themselves be detected as a pattern.Step 4: Handle CAPTCHAs
Browser Use cannot solve CAPTCHAs by itself. For sites that serve CAPTCHAs, you have three options:
| Option | How it works | Cost/complexity |
|---|---|---|
| 2captcha / Anti-Captcha API | Human solvers answer CAPTCHAs via API in 10-30 seconds | Low cost (~$1-3 per 1000), easy to integrate |
| Capsolver / NopeCHA | AI-based CAPTCHA solver, faster than human services | Low cost, very fast, good reCAPTCHA/hCaptcha coverage |
| Managed browser service (BrowserBase, Steel) | Pre-warmed browsers with built-in CAPTCHA handling and residential proxies | Higher cost, zero config |
# Integrating a CAPTCHA solver callback
from browser_use import Agent
import httpx
async def solve_captcha_callback(page):
# Detect and solve reCAPTCHA if present
captcha_present = await page.query_selector(".g-recaptcha")
if captcha_present:
# Get site key
site_key = await page.get_attribute(".g-recaptcha", "data-sitekey")
page_url = page.url
# Submit to 2captcha (example)
async with httpx.AsyncClient() as client:
response = await client.post(
"http://2captcha.com/in.php",
data={"key": "YOUR_2CAPTCHA_KEY", "method": "userrecaptcha",
"googlekey": site_key, "pageurl": page_url}
)
task_id = response.text.split("|")[1]
# Poll for result (2captcha is async)
for _ in range(30):
await asyncio.sleep(5)
result = await client.get(
f"http://2captcha.com/res.php?key=YOUR_2CAPTCHA_KEY&action=get&id={task_id}"
)
if result.text.startswith("OK|"):
token = result.text.split("|")[1]
# Inject the token into the page
await page.evaluate(
f'document.getElementById("g-recaptcha-response").value = "{token}"'
)
breakStep 5: Use a Cloud Browser Service for Hard Sites
For sites with aggressive bot protection (Cloudflare Enterprise, Akamai Bot Manager, PerimeterX), no amount of local stealth configuration will reliably work. These services use hundreds of signals including TLS fingerprinting, IP reputation, and behavioral analytics that are impossible to spoof from a local Playwright instance.
For these sites, route your Browser Use agent through a managed cloud browser service:
- BrowserBase: managed cloud Chromium, residential proxies, CAPTCHA handling, SOC 2 compliant
- Steel.dev: open-source self-hosted alternative, CDP-compatible, works with Browser Use directly
- Browserless: managed Chrome-as-a-service, drops in as a Playwright replacement
# Route Browser Use through BrowserBase (example)
from browser_use.browser.browser import Browser, BrowserConfig
browser = Browser(
config=BrowserConfig(
# Connect to BrowserBase's managed Chrome via CDP
cdp_url="wss://connect.browserbase.com?apiKey=YOUR_BROWSERBASE_KEY",
)
)
agent = Agent(
task="Extract data from protected site",
llm=ChatAnthropic(model="claude-sonnet-4-6"),
browser=browser,
)Quick Reference
- Set a realistic Chrome user-agent string in BrowserConfig extra_chromium_args
- Apply playwright-stealth to every page via a custom BrowserContext subclass
- Add random delays (0.5-2.5s) between actions using a custom Controller
- For CAPTCHAs: use 2captcha, Capsolver, or NopeCHA API integration
- For heavily protected sites (Cloudflare Enterprise): use BrowserBase or Steel.dev as the browser backend
- Never run agents against sites that explicitly prohibit automated access -- check robots.txt and ToS