undetected chromedriver vs nodriver: Which Fits Your Project?
The choice between undetected chromedriver vs nodriver comes down to plumbing, not vibes. nodriver drops the ChromeDriver binary and Selenium entirely and talks to Chrome over a direct WebSocket DevTools Protocol connection. undetected-chromedriver keeps Selenium and patches the driver binary so the classic cdc_ property disappears. Both come from the same author, ultrafunkamsterdam — nodriver is the successor, and undetected-chromedriver is now the legacy path.

📌 TL;DR Executive Summary
- Core Takeaway: nodriver is the maintained successor — no ChromeDriver, no Selenium, direct CDP, AGPL-3.0. undetected-chromedriver survives as a bridge for Selenium code you cannot rewrite yet.
- Key Risk/Challenge: neither tool hides your IP or spoofs fingerprints, and driver-level patching keeps losing ground to protocol-layer leaks. AGPL-3.0’s network clause also applies if you host modified code.
- Recommended Solution: nodriver or its zendriver fork if you want maximum control in code. If the real problem is running many accounts with separate proxies and fingerprints, Send.win folds that layer into the browser itself.
Verdict First: undetected-chromedriver vs nodriver vs Send.win
The two Python libraries solve the driver layer only. Send.win solves the identity layer underneath it and can still be driven by your scripts.
| Dimension | undetected-chromedriver | nodriver | Send.win |
|---|---|---|---|
| Driver tells | Patches the chromedriver binary, renames the cdc_ property | Never injects a driver; direct CDP over WebSocket | Patched-Chromium desktop app and cloud profiles with the Sendwin Stealth engine built in |
| Framework needed | Selenium | Neither — its own async API | None; desktop app or cloud browser, plus a local Automation API on Team |
| IP and fingerprint layer | Not handled — the docs state the package does not hide your IP | Not handled — no fingerprint spoofing claimed | Built-in residential proxies on every plan; canvas, WebGL, audio, fonts and hardware spoofed at engine level |
| Profiles | Whatever user-data-dir you pass | Fresh profile per run unless you pass user_data_dir | Saved profiles, cloud sync across devices, sharing with paid teammates |
| Concurrency | You manage it | You manage it | No local concurrency cap; 3 concurrent cloud sessions on Pro, 9 on Team |
| Price | Free, open source | Free, open source under AGPL-3.0 | 30-day free trial; Pro $19/mo or $6.99/mo annual; Team $49/mo or $20.99/mo annual |
Architecture: a Patched ChromeDriver vs a Direct CDP Connection
Most undetected chromedriver vs nodriver write-ups stall on feature lists. The real difference is what each library touches: one patches a binary on disk and leaves the driver process attached, the other removes the binary from the stack and speaks to Chrome directly.
What undetected-chromedriver actually changes
The library patches the ChromeDriver binary in place with a byte regex that matches {window.cdc.*?;} and overwrites it with a fixed padded string. That removes one fingerprint: the $cdc_asdjflasutopfhvcZLmcfl_ element-cache property ChromeDriver leaves on the document object, present since at least ChromeDriver 75.
The strength and the weakness are the same thing — a pattern match against a literal string. Scripts that test for one known property name lose that check. Scripts that enumerate the properties of document and compare the result with stock Chrome still find something out of place, because the driver is still attached.
What nodriver does instead
nodriver is the official successor from the same author, published under AGPL-3.0. It drops ChromeDriver and Selenium and speaks the DevTools Protocol directly over a WebSocket, so there is no driver process to detect, no cdc_ property to rename, and no driver version to keep aligned with a Chrome update.
It supports chromium, chrome, edge and brave, and can attach to an already-running Chrome debug session. Both libraries are Chrome/Chromium-only in practice and are normally pointed at an installed Chrome binary rather than a bundled Chromium build.
Why the fight moved down a layer
Once the driver goes away, the tells move lower. A Runtime.enable leak — compounded by a Chrome bug in 2025 — caught wrappers regardless of which library was driving the browser. That is why “my wrapper is undetected” stopped being a stable claim, and why library choice matters less than the protocol behavior of the whole stack.
The Detection Layers You Actually Have to Beat
Four layers decide whether a session survives, and no driver library covers all four. Match the symptom to the layer before you rewrite anything.
| Symptom | What triggered it | What fixes it |
|---|---|---|
| One script checks for the cdc_ property and flags you | ChromeDriver’s element cache exposed on the page document | Renaming the property, or never injecting it at all |
| Property enumeration finds driver artifacts | A visible webdriver flag plus driver-owned globals | Removing the injection path — patch the browser, not the page |
| Even raw CDP calls get blocked | Protocol-level leaks such as Runtime.enable behavior | A stack that patches CDP sequences, or a browser that ships the fix at build level |
| Captcha passes in the browser but the server refuses | Turnstile tokens must be redeemed server-side within 300 seconds, once | Correct token handling on your backend; the browser is only half the check |
Cloudflare Turnstile is the clearest example of the last row. Each widget has a sitekey and a secret key, and the token the page receives is a string of up to 2,048 characters that expires after 300 seconds and can be validated only once through the Siteverify API. A scraper that solves the widget and then reuses a token gets rejected no matter how clean the browser looks.
That is also where benchmark numbers get misread. In one May 2026 test of seven stealth browsers against 31 Cloudflare targets with three sweeps each from a residential network, nodriver returned 28 OK and zero blocked cells — the only tool with no blocked results — while rebrowser-playwright matched plain Playwright’s block set. Read that as one author’s setup on one network, not a ranking you can copy into a forecast.
Install and Minimal Code for Each Library
Both install from PyPI: pip install nodriver and pip install undetected-chromedriver. The APIs are not interchangeable — neither exposes page.goto(), so adopting either means rewriting your automation layer, not swapping a launch call.
undetected-chromedriver: the Selenium-shaped version
import undetected_chromedriver as uc
options = uc.ChromeOptions()
options.add_argument("--window-size=1280,800")
driver = uc.Chrome(
options=options,
user_data_dir="./profiles/acct-01", # keep cookies between runs
)
driver.get("https://example.com/login")
driver.find_element("name", "email").send_keys("[email protected]")
driver.find_element("name", "password").send_keys("your-password")
input("Press Enter to close...")
driver.quit()
nodriver: async, with no driver process
import nodriver as uc
async def main():
browser = await uc.start(
user_data_dir="./profiles/acct-01", # omit it and you get a fresh profile per run
browser_executable_path="/path/to/your/chrome",
)
page = await browser.get("https://example.com/login")
await page.sleep(2)
(await page.find("input[name=email]")).send_keys("[email protected]")
await page.save_screenshot("login.png")
if __name__ == "__main__":
uc.loop().run_until_complete(main()) # nodriver runs its own event loop
If your Selenium jobs are still getting caught and a rewrite is not on the table yet, the configuration-level fixes are worth reading first — see Selenium detection fixes.
What Each Option Costs to Run
Both Python libraries are free to download, so the comparison is really about where the money lands instead: proxy bandwidth, the machines holding the sessions, and the engineering hours spent chasing protocol leaks. On license cost alone, undetected chromedriver vs nodriver is a tie — neither charges a fee — so the bill is decided by infrastructure and labor.
| Option | List price | What that price includes |
|---|---|---|
| undetected-chromedriver | No license fee | You supply Chrome, proxies, profile storage, concurrency and maintenance |
| nodriver | No license fee | Same, plus AGPL-3.0 obligations if you modify it and host it |
| zendriver | No license fee | Community fork with standard asyncio and packaged Docker support |
| Send.win Free trial | $0 for 30 days, card required | Desktop app for Windows, macOS and Linux, 10 isolated profiles with unique fingerprints, 10 built-in residential proxies plus 1 GB/month, 10 min/day of cloud browsing |
| Send.win Pro | $19/mo, or $6.99/mo billed annually ($83.88/yr, save 63%) | 150 profiles, 20 residential proxies, 5 GB/month bandwidth, cloud sync for 20 profiles, sharing with up to 20 paid members, unlimited cloud browsing |
| Send.win Team | $49/mo, or $20.99/mo billed annually ($251.88/yr, save 57%) | 500 profiles, 20 residential proxies, 20 GB/month, local Automation API for Selenium, Puppeteer and Playwright, 100 synced profiles, 16 seats, 9 concurrent cloud sessions |
Add-ons on Pro and Team are $6 per GB of proxy bandwidth and $0.05 per extra profile, with custom packages for cloud sessions and seats. A project moving 20 GB a month pays $120 in bandwidth on top of whichever plan carries it. The 30-day trial runs at $0 today, with a 7-day money-back guarantee once it ends.
Licensing: AGPL-3.0 Is Not a Permissive Wheel
nodriver ships under AGPL-3.0. The FSF published that license on 19 November 2007, and it adds a network-use clause on top of GPLv3: if you run a modified version on a public server, you must offer the corresponding source to the people interacting with it. That matters for a hosted scraping or automation service, though it does not forbid commercial use. Read the license text and take proper advice rather than trusting a comparison table.
The surrounding ecosystem is mixed. Patchright is Apache-2.0, Camoufox is MPL-2.0, curl_cffi is MIT, and CloakBrowser pairs an MIT wrapper with a custom binary license. Check the repository’s own license file before you build a commercial service on any of them — the obligation follows from the license, not from anyone’s summary of it.
Profiles, Cookies and Session Persistence
nodriver uses a fresh profile on each run and deletes it on exit unless you pass user_data_dir, in which case the profile is kept. undetected-chromedriver leaves persistence to the Chrome options you supply, usually a separate user-data-dir per account. Both fine for a handful of accounts, both an operations project at scale: you end up building your own profile store, backup and locking layer, because two processes sharing one profile directory corrupt it.
Before you write that store, compare it against a purpose-built profile manager. This antidetect browser tool list shows how much of the work normally lives inside the browser instead of your script.
Send.win treats profiles as first-class objects: the desktop app keeps them on your machine, cloud sync carries logins across devices, and sharing a profile with a paid teammate opens it already signed in without a password changing hands.
nodriver vs zendriver: Maintenance Is Part of the Answer
nodriver remains usable, but it is hard to contribute to: a single maintainer, a largely closed issue tracker, and pull requests carrying critical bug fixes sitting unmerged. That friction is why zendriver forked it under the cdpdriver org. zendriver has passed 1,200 GitHub stars and uses standard asyncio.run() rather than nodriver’s own uc.loop().run_until_complete(), removing one piece of framework lock-in.
Version 0.17.0, released on 26 September 2026, ports the upstream nodriver features and fixes added since the fork and marks two years of the project. The changes matter most when detection is measured from the page side: mouse helpers used to dispatch inconsistent button and pressure state, and clicks and drags now report pressure 0.5 while held and buttons=0 on release, while Tab.mouse_move() with steps moves from the last position instead of jumping from (0,0).
It also fixes verify_cf() never clicking the Turnstile checkbox when the cf-turnstile-response input is a document-level sibling of the challenge’s shadow-DOM host rather than nested inside it, and removes four default arguments (--disable-component-update, --disable-background-networking, --disable-backgrounding-occluded-windows, --disable-renderer-backgrounding) that blocked Widevine DRM on Windows. Minor details, unless you were the one debugging a captcha click that never landed.
Proxy, DNS and Fingerprint Gaps Neither Library Closes
Neither tool hides your IP. undetected-chromedriver’s documentation states it plainly, and nodriver’s claim about staying undetected for most anti-bot solutions says nothing about fingerprinting. Both are driver-layer tools: they change how automation attaches to Chrome, not what a site learns about the machine once it responds.
So you still supply a proxy per identity, stop WebRTC from leaking the real address behind it, and make timezone, locale, language and geolocation match the exit IP. A German residential address with a US-Pacific timezone is a mismatch that gets flagged with no driver tell involved. Cloud sessions raise a further question about where the browser actually runs, which is what a cloud browser server answers in practice.
Send.win covers that layer as part of the product. Every plan includes built-in residential proxies plus bring-your-own HTTP/SOCKS5, and timezone, locale, WebRTC and geolocation follow the proxy’s exit IP automatically. Canvas, WebGL, audio, fonts and hardware are spoofed at the engine level rather than through brittle script injection, and profiles are kept coherent so no two share a fingerprint.
Honest Pros and Cons
undetected-chromedriver
- Pro: keeps your existing Selenium code, so migration cost is close to zero, and it removes the most famous driver tell in one step.
- Pro: free, widely documented, and most failures you hit have been written up before.
- Con: a literal-string patch does not survive property enumeration or protocol-level checks.
- Con: low momentum in 2026 — treat it as a bridge for legacy stacks, not a foundation for new work. No IP or fingerprint handling either.
nodriver
- Pro: no driver process at all, so there is no cdc_ property to find, and no Selenium dependency.
- Pro: the strongest result in one May 2026 sweep of seven tools — 28 OK, zero blocked across 31 Cloudflare targets.
- Con: its own API with no
page.goto(), so adopting it is a rewrite rather than a swap. - Con: AGPL-3.0 network clause if you modify and host it, plus contribution friction that produced the zendriver fork.
Send.win
- Pro: proxy, fingerprint, profile and login management in one product instead of four scripts and a folder of directories.
- Pro: desktop app on Windows, macOS and Linux, or cloud profiles from any device with a free 10 min/day preview.
- Con: a subscription, not a pip install — for a 200-line one-off script a library is cheaper, and the Automation API needs Team.
- Con: proxy bandwidth beyond your plan’s allowance costs $6/GB.
Worth knowing: the rest of the field
| Tool | License and scale | Why it comes up here |
|---|---|---|
| zendriver | Fork of nodriver, 1,200+ stars | Standard asyncio plus fixes for mouse state, Turnstile clicks and extensions on newer Chrome builds |
| Patchright | Apache-2.0, 3.2k stars | Patches Runtime.enable and Target.setAutoAttach sequences, with channel=chrome for a real Chrome TLS shape |
| Camoufox | MPL-2.0, 8.4k stars | Firefox modified at the C level to spoof canvas, WebGL, screen geometry and navigator properties |
| CloakBrowser | MIT wrapper plus custom binary license, 13.5k stars | Patched Chromium with 49 source-level C++ changes and a drop-in Playwright surface |
| curl_cffi | MIT, 6.4 MB wheel | No JavaScript engine at all; Chrome-shaped TLS handshake, version 0.15.0 defaulting to the Chrome 145/146 shape |
| rebrowser-playwright | Last code commit September 2024 | CDP-leak patches on bundled Chromium 136; effectively unmaintained today |
Which Should You Pick?
Use the constraint that is actually binding you, not the one that sounds most technical. In most real projects the undetected chromedriver vs nodriver decision matters far less than the proxy and profile layer sitting underneath either one.
- A working Selenium codebase and no time to rewrite: stay on undetected-chromedriver, fix the tells you can measure, and schedule the move to a CDP stack for when a rewrite is already planned.
- A new Python project where you want full control: use nodriver, or zendriver if you prefer standard asyncio and fixes that keep landing. Budget separately for proxies and profile storage.
- An existing Playwright codebase: a patched Playwright fork fits closer than either library — the patched Playwright forks compared walks through what each one patches.
- Many real accounts rather than one scrape target: the bottleneck is identity. Give every account its own profile, proxy and fingerprint so a block costs you one session instead of the fleet.
- Automation and shared logins in one place: drive Send.win profiles through its local Automation API on Team, so proxy, fingerprint and session are attached before your script connects.
from playwright.sync_api import sync_playwright
CDP_URL = "http://127.0.0.1:PORT" # copy it from the profile's automation settings
with sync_playwright() as p:
browser = p.chromium.connect_over_cdp(CDP_URL)
context = browser.contexts[0]
page = context.pages[0] if context.pages else context.new_page()
page.goto("https://example.com")
🏆 Send.win Verdict
nodriver and undetected-chromedriver both change how your script attaches to Chrome — neither changes what the site learns about the machine or the IP behind it. If your project is one scraper against one target, a library is enough. If it is dozens of accounts, Send.win removes the layer you would otherwise build: residential proxies included, fingerprints spoofed at engine level, profiles that sync and can be shared with a teammate, and a local Automation API on Team when you still want Selenium, Puppeteer or Playwright driving the session.
Try Send.win free today — 30 days at $0 with 10 isolated profiles, 10 built-in residential proxies and a cloud preview that runs 10 minutes a day with nothing to install.
Frequently Asked Questions
Is nodriver a drop-in replacement for undetected-chromedriver?
No. The undetected chromedriver vs nodriver migration is a rewrite, not a swap: nodriver has its own API, no Selenium layer, and no page.goto(). Expect to rewrite element lookups, waits and error handling, so plan the move as a port.
How Send.win Helps With Undetected Chromedriver Vs Nodriver
Send.win is an antidetect browser built for exactly this kind of work — every profile is a clean, isolated identity:
- Isolated profiles – unique fingerprint, separate cookies and storage per profile
- Stealth engine – canvas, WebGL, fonts, and audio spoofed at the engine level
- Desktop app + cloud sessions – native app for Windows, macOS, and Linux, or run profiles in the cloud with no install
- Built-in residential proxies – with automatic timezone, locale, and WebRTC matching
- Team features – share logged-in profiles with teammates without sharing passwords
Try the instant cloud browser demo — no install, no signup — or download the desktop app. The 30-day free trial needs no credit card, and paid plans start at $6.99/month billed annually (see pricing).
Does nodriver hide my IP or fix fingerprinting?
No. undetected-chromedriver’s documentation states it does not hide your IP, and nodriver’s stealth claims concern driver-level detection. You still need a proxy per identity, plus coherent timezone, locale and WebRTC settings to match it.
Why did undetected-chromedriver stop working as well?
Its core fix renames a known property through a byte regex on the driver binary. That defeats scripts checking one literal string, but not scripts that enumerate document properties, and not protocol-layer leaks such as Runtime.enable.
Can I keep my Playwright code and still use nodriver?
No. nodriver is not a Playwright backend and does not expose Playwright’s page API. If you want to keep Playwright calls and gain patched CDP behavior, look at a patched Playwright fork instead.
Which is better against Cloudflare Turnstile in 2026?
The browser is only half the check. Turnstile tokens are up to 2,048 characters, expire after 300 seconds and validate only once through Siteverify, so token reuse fails regardless of driver. On the browser side, nodriver returned zero blocked cells across 31 targets in one May 2026 test.
Do I need Selenium installed to run nodriver?
No. It drops both the ChromeDriver binary and Selenium, connecting straight to Chrome over the DevTools Protocol WebSocket. It supports chromium, chrome, edge and brave, and can attach to an already-running debug session.
Should I use nodriver or zendriver for a new project?
zendriver is the lower-friction option: standard asyncio.run(), active bug fixes, Docker packaging and a 0.17.0 release on 26 September 2026 that ports upstream work and corrects mouse-event state. Check the license file of whichever fork you pick before shipping commercially.
Does nodriver’s AGPL-3.0 license affect my commercial scraping service?
The network clause applies when you modify the software and let users interact with it over a network: you must offer them the corresponding source. It does not forbid commercial use, and it does not bind you if you use it unmodified in a private script. Read the license text and take qualified advice.