# nodriver: Python Browser Automation Without Selenium or WebDriver

> nodriver is the official successor to undetected-chromedriver, a Python library that drives Chromium-based browsers directly over the Chrome DevTools Protocol without any WebDriver binary. It is fully asynchronous and designed to avoid detection by bot-protection systems such as Cloudflare, hCaptcha, and Imperva.

**ultrafunkamsterdam/nodriver** — Successor of Undetected-Chromedriver. Providing a blazing fast framework for web automation, webscraping, bots and any other creative ideas which are normally hindered by annoying anti bot systems like Captcha / CloudFlare / Imperva / hCaptcha 

- Repository: https://github.com/ultrafunkamsterdam/nodriver
- Stars: 4,799 · Forks: 440
- Language: Python
- License: AGPL-3.0
- Published: 2026-09-23 · Updated: 2026-09-23 · Language: en
- Canonical page: https://hysenlabs.com/projects/ultrafunkamsterdam-nodriver

## What problem nodriver solves

Web scraping and browser automation tools that rely on WebDriver are detectable by sophisticated anti-bot systems. WebDriver injects JavaScript properties into the browser environment that fingerprinting code can detect. Selenium adds detectable markers through its ChromeDriver binary. Bot-protection services such as Cloudflare, Imperva, and hCaptcha use these signals to identify and block automated browsers.

nodriver communicates directly with Chromium-based browsers using the Chrome DevTools Protocol (CDP), bypassing WebDriver entirely. The README describes this as providing better resistance against web application firewalls (WAFs) while also producing a performance boost from the direct communication path. The project dropped Selenium as a dependency, which removes the chromedriver binary requirement.

The README positions nodriver as the official successor to undetected-chromedriver, the previous project from the same author. undetected-chromedriver patched ChromeDriver at the binary level. nodriver takes the more direct approach of not using ChromeDriver at all. A utility function is provided to convert a running undetected-chromedriver instance into a nodriver Browser instance for migrations.

The library is fully asynchronous, built on Python's asyncio. This changes the programming model: all browser operations are coroutines that must be awaited. The README notes that this gives massive performance improvements and more detailed control possibilities compared to the synchronous model of undetected-chromedriver.

## Installing nodriver and a first automation script

nodriver requires Python and Chrome (or a compatible Chromium-based browser) installed on the machine. Installation via pip:

```default
pip install nodriver
```

To update an existing installation:

```default
pip install -U nodriver
```

A minimal script from the README demonstrates the basic structure:

```python
import nodriver as uc

async def main():

    browser = await uc.start()
    page = await browser.get('https://www.nowsecure.nl')

    ... further code ...

if __name__ == '__main__':
    # since asyncio.run never worked (for me)
    uc.loop().run_until_complete(main())
```

The `uc.start()` call launches the browser with built-in best-practice defaults. Each run uses a fresh browser profile and cleans it up on exit. The `browser.get()` call navigates to a URL and returns a Tab object. The README recommends `uc.loop().run_until_complete(main())` over `asyncio.run(main())` for the entry point.

## Element selection and tab operations

nodriver provides several methods for finding elements in a page. The `tab.find(text)` method searches by text content. The `tab.find_all(text)` method returns all matching elements. The `tab.select(selector)` method accepts CSS selectors, and `tab.select_all(selector)` returns all matches.

All of these methods retry for the duration of a configurable timeout, which means they also function as wait conditions. An `await tab.select('body')` waits until the body element is present, indicating the page has loaded. The README notes that `tab.find()` does not naively return the first element with matching text but instead returns the candidate with the closest matching text length. This prevents a search for `accept all` from returning a script tag that happens to contain those words.

Element searches include iframe content by default, following a flat connection model introduced in version 0.50.1. The `await tab.get_frames()` method returns inspectable iframe objects. The `tab.xpath(selector)` method allows element lookup via XPath selectors. The `tab.cf_verify()` method attempts to find and click a Cloudflare verification checkbox, though the README notes this requires the `opencv-python` package and is currently limited to English-language challenges.

Additional tab utilities include `tab.get_local_storage()` and `tab.set_local_storage(dict)` for reading and writing the local storage of the current page, and `tab.bypass_insecure_connection_warning()` for pages with invalid certificates.

## Custom browser configuration

nodriver supports multiple approaches to configure the browser at startup. The simplest uses keyword arguments to `start()`:

```python
from nodriver import *

browser = await start(
    headless=False,
    user_data_dir="/path/to/existing/profile",
    browser_executable_path="/path/to/some/other/browser",
    browser_args=['--some-browser-arg=true', '--some-other-option'],
    lang="en-US"
)
tab = await browser.get('https://somewebsite.com')
```

Specifying `user_data_dir` preserves the profile across runs instead of deleting it on exit. This is useful for reusing login sessions. The `browser_executable_path` parameter lets nodriver drive Edge or Brave instead of the default Chrome installation.

The `start(expert=True)` mode makes additional modifications: it disables web security, disables origin trials, and ensures shadow DOM roots are always open. The README cautions that expert mode makes the browser more detectable because those settings deviate from a normal browser configuration.

Cookies can be saved to and loaded from a file, which the README describes as a way to avoid repeating tedious login steps. The `add_handler(someEvent, callback)` method registers event listeners for CDP events. The callback may accept a single event argument or both the event and the tab.

## Platform requirements and headless server usage

nodriver works with Chromium, Chrome, Edge, and Brave. The browser must be installed on the machine running the script, preferably in the default location. On headless machines such as AWS EC2 instances or other environments without a display, the README recommends using Xvfb (X virtual framebuffer) to emulate a screen. Alternatively, nodriver can run in headless mode, though the README does not detail the specific headless configuration.

The library is listed in pyproject.toml as version 0.50.3. The dependencies are minimal: no Selenium, no chromedriver binary. The only external requirement is a Chromium-based browser on the system path.

The AGPL-3.0 license is a strong copyleft license that requires any software using or distributing nodriver to release its source code under the same license if distributed externally. This is a significant constraint for commercial closed-source products. The README acknowledges the project's legal uncertainty by noting that running from a bad IP or datacenter may still cause captchas, and warns that "with great power comes" responsibility, recommending users avoid giving anti-bot systems reasons to escalate their countermeasures.

## nodriver versus Playwright and Selenium

Playwright is a browser automation library from Microsoft that supports Chrome, Firefox, and WebKit. It provides both synchronous and async Python APIs and uses its own browser-driver binaries rather than WebDriver. Playwright is not specifically designed to avoid bot detection: it can be fingerprinted through the same CDP mechanisms that detect Selenium, though fingerprinting tools can be used alongside it.

Selenium is the established browser automation standard with broad language support and a large ecosystem. It uses WebDriver, which is detectable. The selenium-driverless project is a fork that drops the WebDriver protocol in favor of CDP, similar to nodriver's approach.

nodriver's differentiation from Playwright is its explicit focus on anti-detection. The built-in configuration defaults are chosen to minimize fingerprinting signals. Playwright is a better choice for general-purpose browser testing where detection is not a concern and multi-browser support (Firefox, WebKit) is needed. nodriver is the better fit when the target site actively blocks automated browsers and the test environment is Chromium-only.

## Event handling, network monitoring, and example scripts

The `examples/` directory in the repository contains several example scripts that demonstrate real-world use cases. `demo.py` provides a basic demonstration of the library. `network_monitor.py` shows how to monitor network requests made by the browser. `network_response_body.py` captures response bodies from network requests. `imgur_upload_image.py` demonstrates file upload automation. `mouse_drag_boxes.py` shows mouse drag interactions.

For network monitoring, CDP events allow the script to intercept requests and responses. The `add_handler` method registers a callback for a specific CDP event type. The `tab.get_frames()` method returns frames that can be individually inspected, which matters for pages that load content in iframes (such as payment widgets or embedded social media).

The version 0.50.1 release notes describe the switch to flat mode connections, where iframes are included in most operations by default. This change required significant internal rewriting. The README recommends testing thoroughly, especially for large projects, because the flat mode change affects find() and select() behavior.

## Conclusion

nodriver is the right choice for Python developers who need to automate a Chromium-based browser and who face bot-protection systems that block Selenium-based tools. It requires Chrome or a compatible browser installed on the machine, runs only in an async Python context, and is licensed under AGPL-3.0, which requires derivative works to be open-source. Teams building commercial closed-source automation pipelines must evaluate the AGPL-3.0 terms carefully before using it. The project's last commit was on 2026-05-13, and its pyproject.toml lists version 0.50.3.

## FAQ

### How do I install nodriver?

Install nodriver with `pip install nodriver`. To update, run `pip install -U nodriver`. A Chromium-based browser (Chrome, Edge, or Brave) must be installed on the machine. The library requires Python with asyncio support.

### How does nodriver differ from Selenium?

nodriver communicates directly with the browser via the Chrome DevTools Protocol without a WebDriver binary or Selenium. This removes the detectable WebDriver markers that anti-bot systems look for. nodriver is fully asynchronous and supports only Chromium-based browsers, while Selenium supports multiple browsers and both sync and async usage.

### How does nodriver compare to undetected-chromedriver?

The README describes nodriver as the official successor to undetected-chromedriver. undetected-chromedriver patched ChromeDriver at the binary level. nodriver drops ChromeDriver entirely, communicating via CDP directly, which the README says gives better resistance against web application firewalls and a significant performance improvement. nodriver is also fully asynchronous, unlike its predecessor.

### What are the alternatives to nodriver?

Playwright is the main alternative for general browser automation and supports Chrome, Firefox, and WebKit. For anti-detection in particular, selenium-driverless takes a similar CDP-direct approach to nodriver. The README provides a utility to convert a running undetected-chromedriver instance to a nodriver Browser instance.

## Sources

- [Issues](https://github.com/ultrafunkamsterdam/nodriver/issues)
- [License: AGPL-3.0](https://github.com/ultrafunkamsterdam/nodriver/blob/main/LICENSE)
- [README](https://github.com/ultrafunkamsterdam/nodriver/blob/main/README.md)
- [ultrafunkamsterdam/nodriver on GitHub](https://github.com/ultrafunkamsterdam/nodriver)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/ultrafunkamsterdam-nodriver
