What Is Undetected ChromeDriver and Why Do You Need It?
Selenium undetected chromedriver is a Python library that patches the standard ChromeDriver binary to remove the automation fingerprints that websites use to detect and block Selenium bots. It works by modifying the chromedriver executable at the byte level — stripping telltale variables like navigator.webdriver, renaming CDP (Chrome DevTools Protocol) endpoints, and randomizing other signatures that anti-bot systems flag. If your Selenium scripts keep hitting CAPTCHAs or getting IP-banned, this selenium undetected chromedriver guide walks you through installation, setup, working Python code, and the hard limits you will eventually hit.
Why Standard Selenium Gets Detected
Before diving into the fix, you need to understand what is actually getting you caught. Standard Selenium + ChromeDriver leaves a trail of detectable artifacts that even basic anti-bot scripts can spot in milliseconds.
The navigator.webdriver Flag
The most obvious tell is navigator.webdriver, a JavaScript property that returns true when a browser is controlled by automation software. Every major anti-bot system — Cloudflare, DataDome, PerimeterX, Akamai — checks this property as step one. Standard ChromeDriver sets it to true by default, and simply overriding it via JavaScript execute doesn’t always work because the check can happen before your script runs.
Chrome DevTools Protocol (CDP) Leaks
ChromeDriver communicates with Chrome using the DevTools Protocol over a WebSocket connection. Anti-bot systems can detect the presence of these debugging endpoints — specifically, they look for open /json/version and /json endpoints on predictable ports. If these endpoints respond, the site knows automation tools are attached.
ChromeDriver Binary Signatures
The chromedriver binary itself contains hardcoded strings like cdc_ variables and specific function names that JavaScript on the page can detect by enumerating window properties or checking the DOM for injected elements. These strings are consistent across versions, making them reliable detection vectors.
User Agent and Header Anomalies
Selenium’s default configuration often sends headers that don’t match a real browser session. Missing Accept-Language headers, inconsistent sec-ch-ua values, or a user agent string that doesn’t match the actual Chrome version all raise flags. To understand the full scope of what sites check, read our deep dive on Selenium browser fingerprinting.
How Undetected ChromeDriver Works Under the Hood
The undetected-chromedriver library (often imported as uc) takes a fundamentally different approach from simple flag overrides. Instead of trying to mask automation signals after the browser launches, it patches the chromedriver binary itself before Chrome ever starts.
Binary Patching
The library downloads the correct chromedriver version for your installed Chrome, then modifies the binary file directly. It finds and replaces the cdc_ variable names with random strings of the same length, so the binary still functions correctly but no longer contains the signatures that detection scripts search for.
Runtime Modifications
Beyond binary patching, undetected-chromedriver also:
- Suppresses the
navigator.webdriverflag before any page JavaScript executes - Removes the
enable-automationChrome switch - Blocks the
AutomationControlledChrome info bar - Closes the DevTools debugging port after initialization to prevent CDP detection
- Sets realistic default window sizes and positions
Automatic Version Management
One of the biggest maintenance headaches with standard Selenium is keeping chromedriver in sync with your Chrome version. Undetected-chromedriver handles this automatically — it detects your installed Chrome version, downloads the matching chromedriver, patches it, and caches the result for future runs.
Installation and Setup
Getting started requires Python 3.7+ and Google Chrome installed on your system. Here is the complete setup process.
Step 1: Install the Package
pip install undetected-chromedriver
This installs the library along with its dependencies, including Selenium itself. You do not need to install Selenium separately.
Step 2: Basic Usage
import undetected_chromedriver as uc
# Create an undetected Chrome instance
driver = uc.Chrome()
# Navigate to a site with bot detection
driver.get("https://nowsecure.nl")
# Check the result
print(driver.title)
# Clean up
driver.quit()
That is the minimal working example. The library handles chromedriver download, patching, and launching with anti-detection settings automatically.
Step 3: Custom Configuration
For production use, you will want more control over the browser configuration:
import undetected_chromedriver as uc
options = uc.ChromeOptions()
# Set a specific user agent
options.add_argument("--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36")
# Set window size to a common resolution
options.add_argument("--window-size=1920,1080")
# Disable images for faster loading (optional)
options.add_argument("--blink-settings=imagesEnabled=false")
# Run in headless mode (use with caution — see limitations)
# options.add_argument("--headless=new")
# Set a custom profile directory for persistent sessions
options.add_argument("--user-data-dir=/tmp/chrome-profile")
driver = uc.Chrome(options=options)
driver.get("https://example.com")
driver.quit()
Working Python Code Examples
Here are practical, runnable examples for common scraping and automation scenarios.
Example 1: Scraping a Cloudflare-Protected Site
import undetected_chromedriver as uc
import time
def scrape_protected_site(url):
driver = uc.Chrome()
try:
driver.get(url)
# Wait for Cloudflare challenge to resolve
time.sleep(5)
# Check if we passed the challenge
if "Just a moment" not in driver.page_source:
print("Successfully bypassed Cloudflare!")
# Extract page content
title = driver.title
body_text = driver.find_element("tag name", "body").text
return {"title": title, "content": body_text[:500]}
else:
print("Still stuck on Cloudflare challenge")
return None
finally:
driver.quit()
result = scrape_protected_site("https://example-protected-site.com")
if result:
print(f"Title: {result['title']}")
print(f"Content preview: {result['content']}")
Example 2: Handling Login Forms With Anti-Bot Protection
import undetected_chromedriver as uc
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import time
import random
def human_type(element, text):
"""Simulate human-like typing with random delays."""
for char in text:
element.send_keys(char)
time.sleep(random.uniform(0.05, 0.15))
def login_to_site(url, username, password):
options = uc.ChromeOptions()
options.add_argument("--window-size=1920,1080")
driver = uc.Chrome(options=options)
try:
driver.get(url)
# Wait for the page to fully load
wait = WebDriverWait(driver, 15)
# Find and fill the username field
username_field = wait.until(
EC.presence_of_element_located((By.NAME, "username"))
)
human_type(username_field, username)
time.sleep(random.uniform(0.5, 1.5))
# Find and fill the password field
password_field = driver.find_element(By.NAME, "password")
human_type(password_field, password)
time.sleep(random.uniform(0.3, 0.8))
# Click the submit button
submit_btn = driver.find_element(By.CSS_SELECTOR, "button[type='submit']")
submit_btn.click()
# Wait for redirect after login
time.sleep(3)
print(f"Current URL: {driver.current_url}")
return driver.current_url
finally:
driver.quit()
login_to_site("https://example.com/login", "user@example.com", "password123")
Example 3: Multi-Page Scraping With Session Persistence
import undetected_chromedriver as uc
from selenium.webdriver.common.by import By
import json
import time
def scrape_multiple_pages(base_url, num_pages=5):
options = uc.ChromeOptions()
options.add_argument("--user-data-dir=/tmp/scrape-session")
driver = uc.Chrome(options=options)
all_data = []
try:
for page in range(1, num_pages + 1):
url = f"{base_url}?page={page}"
driver.get(url)
# Random delay between pages to mimic human behavior
time.sleep(2 + (page * 0.5))
# Extract data from the page
items = driver.find_elements(By.CSS_SELECTOR, ".product-card")
for item in items:
try:
name = item.find_element(By.CSS_SELECTOR, ".title").text
price = item.find_element(By.CSS_SELECTOR, ".price").text
all_data.append({"name": name, "price": price})
except Exception:
continue
print(f"Page {page}: found {len(items)} items")
finally:
driver.quit()
# Save results
with open("scraped_data.json", "w") as f:
json.dump(all_data, f, indent=2)
return all_data
results = scrape_multiple_pages("https://example-shop.com/products")
print(f"Total items scraped: {len(results)}")
Common Detection Bypasses Explained
Even with undetected-chromedriver, you may need additional techniques for heavily protected sites. Here are the most effective supplementary bypasses.
Fixing navigator.webdriver Persistence
Some detection scripts check navigator.webdriver at different points during page load. Undetected-chromedriver handles this, but for extra safety you can add a CDP command:
import undetected_chromedriver as uc
driver = uc.Chrome()
# Additional webdriver property suppression via CDP
driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {
"source": """
Object.defineProperty(navigator, 'webdriver', {
get: () => undefined
});
"""
})
driver.get("https://target-site.com")
Realistic Browser Fingerprinting
Websites check dozens of browser fingerprint attributes beyond just navigator.webdriver. Make your browser look more realistic:
import undetected_chromedriver as uc
driver = uc.Chrome()
# Spoof plugins, languages, and platform details
driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {
"source": """
// Override plugins to match a real browser
Object.defineProperty(navigator, 'plugins', {
get: () => [1, 2, 3, 4, 5]
});
// Set realistic language preferences
Object.defineProperty(navigator, 'languages', {
get: () => ['en-US', 'en']
});
// Override WebGL vendor/renderer
const getParameter = WebGLRenderingContext.prototype.getParameter;
WebGLRenderingContext.prototype.getParameter = function(parameter) {
if (parameter === 37445) return 'Intel Inc.';
if (parameter === 37446) return 'Intel Iris OpenGL Engine';
return getParameter.apply(this, arguments);
};
"""
})
driver.get("https://target-site.com")
Proxy Rotation for IP-Based Detection
Fingerprinting is only half the battle. Sites also track IP addresses and flag rapid requests from a single source:
import undetected_chromedriver as uc
def create_driver_with_proxy(proxy_address):
options = uc.ChromeOptions()
options.add_argument(f"--proxy-server={proxy_address}")
return uc.Chrome(options=options)
# Rotate through proxies for each session
proxies = [
"http://proxy1.example.com:8080",
"http://proxy2.example.com:8080",
"http://proxy3.example.com:8080"
]
for i, proxy in enumerate(proxies):
driver = create_driver_with_proxy(proxy)
driver.get("https://target-site.com")
print(f"Session {i+1} via {proxy}: {driver.title}")
driver.quit()
Limitations: When Undetected ChromeDriver Isn’t Enough
Undetected-chromedriver is a solid first step, but it has real limitations you should know about before building production systems around it.
Advanced Fingerprinting Beats Binary Patches
Modern anti-bot systems like Cloudflare Turnstile, DataDome, and Kasada go far beyond checking navigator.webdriver. They analyze:
- Canvas fingerprinting — rendering differences between real and automated browsers
- WebGL fingerprinting — GPU-specific rendering patterns
- Audio context fingerprinting — subtle differences in audio processing
- Behavioral analysis — mouse movements, scroll patterns, typing cadence
- TLS fingerprinting (JA3/JA4) — the SSL/TLS handshake pattern reveals automation tools
- HTTP/2 fingerprinting — frame ordering and priority settings differ from real browsers
Undetected-chromedriver does not address most of these. A site using fingerprinting at this level will still detect your automation. For the full picture on how these systems work, see our guide on bypassing anti-bot detection.
Headless Mode Is Still Risky
Running undetected-chromedriver with --headless=new reintroduces several detection vectors. Headless Chrome behaves differently from headed Chrome in measurable ways — screen dimensions, rendering behavior, and missing GPU compositing all serve as red flags. If stealth matters, run headed with a virtual display (Xvfb on Linux) instead.
Version Lag and Breaking Changes
The library depends on a cat-and-mouse game with Google. When Chrome releases a new version that changes internal structures, undetected-chromedriver can break until the maintainer pushes an update. In production, this means random failures every six weeks when Chrome auto-updates.
Single Browser Profile Limits
Each undetected-chromedriver instance shares the same machine fingerprint — same fonts, same screen resolution, same GPU signature. Running multiple instances from one machine creates a cluster of identical fingerprints that anti-bot systems correlate easily. Proper session isolation requires separate browser profiles with distinct fingerprints, not just separate chromedriver processes.
No Built-In Profile Management
If you need to manage multiple accounts — each with its own cookies, fingerprint, and proxy — undetected-chromedriver gives you nothing. You are on your own for session storage, fingerprint randomization, and proxy assignment. Scaling past a handful of sessions becomes an engineering project in itself.
When You Need a More Robust Solution
Undetected-chromedriver works for one-off scripts and light automation against moderately protected sites. But when you need to operate at scale — managing dozens or hundreds of browser sessions, each with unique fingerprints, persistent cookies, and assigned proxies — patching chromedriver is not enough.
The Multi-Account Problem
Consider a real scenario: you manage 50 e-commerce accounts, each needing its own identity. With undetected-chromedriver, you would need to:
- Manually manage 50 Chrome profiles with separate user data directories
- Assign and rotate proxies per profile
- Generate and maintain unique fingerprints for each
- Handle cookie persistence across sessions
- Hope that Chrome updates don’t break everything overnight
This is where purpose-built antidetect browsers and automation platforms justify their existence. They solve the infrastructure problem so you can focus on the automation logic.
Send.win’s Automation API Approach
Send.win’s Automation API connects your existing Selenium, Puppeteer, or Playwright scripts to fully isolated browser profiles — each with its own fingerprint, cookies, proxy, and timezone configuration. Instead of patching chromedriver and hoping detection systems don’t update their checks, you connect to a profile’s local automation endpoint and script against a browser that already has proper fingerprint isolation baked in.
The Automation API is available on both the Pro plan ($9.99/mo, or $6.99/mo annually) and the Team plan ($29.99/mo, or $20.99/mo annually). Pro gives you 150 profiles and 5GB proxy bandwidth; Team scales to 500 profiles, 20GB bandwidth, and 16 team seats. Both include a 30-day free trial with no credit card required.
For teams that need browser sessions without installing anything locally, Send.win also offers cloud browser sessions — run profiles entirely in the cloud, accessible from any device.
Undetected ChromeDriver vs Antidetect Browser: Quick Comparison
| Feature | Undetected ChromeDriver | Send.win Automation API |
|---|---|---|
| Setup complexity | Low (pip install) | Low (connect to profile endpoint) |
| Browser fingerprint isolation | Minimal (binary patch only) | Full (canvas, WebGL, audio, fonts, timezone) |
| Multi-profile management | Manual | Built-in (up to 500 profiles) |
| Proxy management | Manual per-instance | Per-profile with bandwidth included |
| Cookie persistence | Manual user-data-dir | Automatic per profile |
| Headless support | Risky (detectable) | Cloud sessions available |
| Selenium/Puppeteer/Playwright | Selenium only | All three supported |
| Chrome update resilience | Breaks on major updates | Managed by platform |
| Cost | Free (open source) | From $6.99/mo (annual Pro) |
Best Practices for Using Undetected ChromeDriver
If undetected-chromedriver is the right tool for your use case, follow these practices to maximize your success rate.
Always Add Human-Like Delays
Never hammer pages with zero-delay requests. Use randomized delays between 2-7 seconds for page loads and 0.05-0.2 seconds between keystrokes. Pattern-free timing is harder for behavioral analysis to flag.
Keep Chrome and the Library Updated
Run pip install --upgrade undetected-chromedriver regularly. The library’s effectiveness depends on keeping pace with Chrome’s changes and new detection methods.
Use Realistic Browser Profiles
Set window sizes to common resolutions (1920×1080, 1366×768). Include proper language headers. Load a real Chrome profile with history and extensions if possible — empty profiles look suspicious.
Implement Proper Error Handling
import undetected_chromedriver as uc
from selenium.common.exceptions import TimeoutException, WebDriverException
import time
def resilient_scrape(url, max_retries=3):
for attempt in range(max_retries):
driver = None
try:
driver = uc.Chrome()
driver.set_page_load_timeout(30)
driver.get(url)
# Verify page loaded successfully
if "blocked" in driver.title.lower() or "captcha" in driver.page_source.lower():
print(f"Attempt {attempt + 1}: Detected block, retrying...")
time.sleep(10 * (attempt + 1)) # Exponential backoff
continue
return driver.page_source
except TimeoutException:
print(f"Attempt {attempt + 1}: Page load timeout")
except WebDriverException as e:
print(f"Attempt {attempt + 1}: WebDriver error: {e}")
finally:
if driver:
driver.quit()
return None
html = resilient_scrape("https://target-site.com")
if html:
print(f"Successfully scraped {len(html)} characters")
else:
print("All attempts failed")
Monitor Detection Status
Test your setup against detection test sites before running against real targets:
import undetected_chromedriver as uc
driver = uc.Chrome()
# Test against common detection checks
test_sites = [
"https://nowsecure.nl",
"https://bot.sannysoft.com",
"https://abrahamjuliot.github.io/creepjs/"
]
for site in test_sites:
driver.get(site)
print(f"{site}: {driver.title}")
import time
time.sleep(3)
driver.quit()
🏆 Send.win Verdict
Undetected-chromedriver is a useful free tool for light automation against basic bot detection. But it only patches one layer — the chromedriver binary — while modern anti-bot systems fingerprint canvas, WebGL, audio, TLS handshakes, and behavioral patterns. For production-grade automation that needs real fingerprint isolation, persistent profiles, and multi-account management, Send.win’s Automation API lets you connect Selenium, Puppeteer, or Playwright scripts to fully isolated browser profiles starting at $6.99/month.
Try Send.win free today — 30-day trial, no credit card, 150 profiles on Pro with full Automation API access.
Frequently Asked Questions
Is undetected-chromedriver legal to use?
The library itself is legal — it is open-source software that modifies how ChromeDriver operates. However, what you do with it matters. Web scraping may violate a site’s Terms of Service, and automating actions on platforms that prohibit bots can lead to account bans. Always check the legal requirements for your specific use case and jurisdiction.
Does undetected-chromedriver work with headless Chrome?
It can run in headless mode using --headless=new, but headless Chrome has distinct behavioral patterns that sophisticated detection systems can identify. For better stealth, run in headed mode with a virtual display (Xvfb on Linux) or use cloud browser sessions that handle rendering server-side.
Why does undetected-chromedriver fail on some sites?
Advanced anti-bot systems like Cloudflare Turnstile, DataDome, and Kasada use multi-layered detection that goes beyond the chromedriver binary. They analyze canvas rendering, WebGL output, TLS fingerprints, mouse movements, and dozens of other signals that binary patching cannot spoof. These sites require full fingerprint isolation, not just a patched driver.
Can I run multiple undetected-chromedriver instances simultaneously?
Yes, but each instance shares your machine’s hardware fingerprint. Anti-bot systems can correlate multiple sessions originating from the same GPU, screen configuration, and font set. For true multi-session isolation, you need separate browser profiles with distinct fingerprints — something antidetect browsers like Send.win’s Sendwin Browser handle natively.
How often does undetected-chromedriver break?
Roughly every six weeks, aligned with Chrome’s major release cycle. When Chrome updates its internal structures, the library’s binary patching may fail until the maintainer pushes a compatible update. Pinning your Chrome version can buy time, but creates its own detection risk since outdated browser versions are suspicious.
What is the difference between undetected-chromedriver and Selenium Stealth?
Selenium Stealth (the selenium-stealth package) injects JavaScript to override detection properties at runtime, while undetected-chromedriver patches the chromedriver binary before launch. Undetected-chromedriver is generally more effective because it removes signals at a lower level, before the browser even starts. Many developers use both together for maximum coverage.
Does undetected-chromedriver support Playwright or Puppeteer?
No. Undetected-chromedriver is specifically a Selenium/ChromeDriver wrapper. For Playwright stealth, you would use playwright-stealth or playwright-extra. For Puppeteer, there is puppeteer-extra-plugin-stealth. Alternatively, Send.win’s Automation API supports all three frameworks against the same isolated browser profiles.
How does undetected-chromedriver compare to using a residential proxy?
They solve different problems. Residential proxies mask your IP address and geolocation, while undetected-chromedriver patches browser automation signals. Most serious automation setups need both — a clean IP from a residential proxy and a clean browser fingerprint. Using one without the other still leaves you exposed to half the detection surface.
Automate Selenium Undetected Chromedriver Guide With Send.win
Send.win pairs isolated, fingerprint-managed browser profiles with a full Automation API, so your scripts run in profiles that look and behave like real, separate users:
- Selenium, Puppeteer & Playwright support – drive any profile programmatically (Team plan)
- Isolated profiles – each with its own fingerprint, cookies, and storage
- Built-in residential proxies – with automatic timezone, locale, and WebRTC matching
- Desktop app for Windows, macOS & Linux – plus cloud sessions when you don’t want a local install
Try the instant cloud browser demo — no install, straight from your browser. Then compare plans: a 30-day free trial with no credit card, and paid plans from $6.99/month billed annually.