Undetected ChromeDriver: Bypassing Bot Detection by Patching Selenium at the Binary Level
Hook
Most anti-detection tools try to remove navigator.webdriver after Chrome sets it. This library ensures it never gets set in the first place by patching the driver binary before launch.
Context
Anyone who’s tried to automate Chrome with Selenium has hit the wall: legitimate testing blocked by Cloudflare, DataDome, or Imperva. The core issue is that Selenium injects automation markers into the browser environment—variables like navigator.webdriver, altered user agent properties, and detectable CDP runtime signatures. Anti-bot systems fingerprint these markers instantly.
Traditional workarounds involve running JavaScript to delete these variables after page load, using Chrome DevTools Protocol commands to mask automation, or configuring obscure browser flags. The problem? These approaches operate after the fact. By the time your script executes delete navigator.webdriver, advanced protection systems have already read it and flagged your session. Undetected ChromeDriver takes a different approach: it modifies the ChromeDriver binary itself before Chrome ever starts, preventing automation signatures from being injected rather than scrambling to remove them.
Technical Insight
The architecture here is deceptively simple but clever. Instead of wrapping Selenium with post-launch patches, undetected-chromedriver intercepts the problem at its source. When you instantiate the driver, the library downloads the appropriate ChromeDriver binary for your Chrome version (handling version matching automatically), then patches the executable. The patches target specific byte sequences in the binary that correspond to code responsible for injecting automation variables. By neutering these injection routines before the driver process starts, the browser environment remains clean from the beginning.
Basic usage demonstrates the zero-config philosophy:
import undetected_chromedriver as uc
driver = uc.Chrome()
driver.get('https://nowsecure.nl') # A bot-detection test site
driver.save_screenshot('success.png')
This code can bypass detection on sites that would block standard Selenium. No CDP commands, no JavaScript execution, no elaborate flag configuration. The library handles ChromeDriver versioning, binary patching, and process management transparently.
The library also adds convenience methods missing from standard Selenium. The click_safe() method on WebElements attempts to avoid detection triggers:
element = driver.find_element('css selector', '#submit-button')
element.click_safe() # More cautious than element.click()
For navigating complex DOMs served across multiple frames, version 3.5.0 introduced find_elements_recursive(). This helps search through iframe boundaries, solving the problem of content split across frame contexts.
The recent 3.4.0 rewrite changed the detection-evasion strategy fundamentally. Earlier versions removed and renamed automation variables. The current approach prevents variable injection entirely by patching the driver binary’s injection code paths. The README emphasizes this architectural shift: “instead of removing and renaming variables, we just keep them, but prevent them from being injected in the first place.”
Headless mode deserves special attention. The library historically didn’t support headless operation because headless Chrome has distinct fingerprinting characteristics. However, version 3.4.5 added experimental headless bypass capabilities. The README explicitly states headless is “unsupported officially” but notes “IT IS NOW UNDETECTED AS WELL (but still unsupported).” This means headless might work for your use case, but you’re in unsupported territory—expect breakage without warning.
File management improved significantly in 3.4.0. Earlier versions created a new randomly-named ChromeDriver binary for each session, littering directories with {randomstring}_chromedriver.exe files. The current version uses a single undetected_chromedriver.exe binary, cleaning up automatically.
Gotcha
The library’s biggest limitation is prominently warned in the README: it does not hide your IP address. The author emphasizes this in all caps with repetition for good reason. If you’re running from a datacenter—AWS, DigitalOcean, even smaller VPS providers—many sites will block you regardless of how invisible your ChromeDriver appears. The README includes side-by-side screenshots showing the same code passing from a residential IP and failing from a datacenter. Bot detection is multi-layered: even with perfect browser fingerprinting, IP reputation screening catches datacenter sources. Similarly, residential IPs with low reputation will fail. This tool solves the browser automation detection problem but doesn’t touch network-layer detection.
Headless mode’s experimental status is another serious constraint. While recent versions added headless bypass, the library provides no guarantees. Your headless automation might work perfectly today and break completely with the next Chrome update. If you need reliable headless operation for CI/CD pipelines or server deployments, this uncertainty is a dealbreaker.
Version stability is also worth considering. The library requires Selenium 4.9+ as of version 3.5.0, explicitly dropping support for older Selenium versions. The 3.4.0 release introduced breaking changes with the detection mechanism rewrite. The author removed several constructor kwargs in 3.5.0 (service_args, service_creationflags, service_log_path), potentially breaking existing code. This isn’t a criticism—the rapid iteration is necessary to keep pace with detection systems—but it means you can’t treat this as a stable dependency. Pin your versions carefully and test thoroughly before upgrading production systems.
Verdict
Use if: You’re automating legitimate browser interactions (end-to-end testing, monitoring, accessibility tools) from residential or high-reputation IPs and you’re hitting bot detection walls with standard Selenium. This library excels at testing scenarios where your application sits behind Cloudflare or similar protection and you need to verify user flows work correctly. It’s also suitable for small-to-medium scale data collection where you have clean IP sources and need to interact with JavaScript-heavy sites that block obvious automation. Skip if: You’re running from datacenter IPs without residential proxies, you require guaranteed headless operation for CI/CD systems, or you need API stability across months without code changes. For large-scale scraping, the combination of IP limitations and potential version breakage makes this unsuitable. Consider Playwright with stealth plugins or lower-level approaches if you need more control or better long-term stability.