proxy-compare.com

Blog · How it works

How to use a proxy in nodriver

Proxy Compare attached a dead proxy to nodriver 0.50.3 by all three documented routes on 27 Aug 2026 and confirmed each applies, by reading the page body rather than the navigation result. It also fixes the encoding bug that stops the package importing on Python 3.14.

3 Sept 2026 · 4 min read

Point in time. The figures below were read on 27 Aug 2026 and are not updated after publication. For current numbers see the comparison table.

On this page (7)
How to use a proxy in nodriver

What is nodriver?

nodriver is the successor to undetected-chromedriver, written by the same author. The change that matters is what it removed: it does not use Selenium or WebDriver at all, and talks to Chrome directly over the DevTools Protocol.

That matters because WebDriver is itself a detectable signal. A browser being driven by ChromeDriver differs from one a person is using, in ways that no amount of patching the driver fully hides. Dropping the driver removes the problem rather than concealing it, and it makes startup considerably faster.

The cost is a different API. nodriver is async throughout and its own thing, so a Selenium or Playwright script does not port across; you rewrite.

Attaching a proxy

nodriver documents three ways:

python
import nodriver as uc

# 1. a browser argument at start
browser = await uc.start(browser_args=["--proxy-server=http://host:port"])

# 2. the same argument through Config
cfg = uc.Config()
cfg.add_argument("--proxy-server=http://host:port")

# 3. a per-context parameter
tab = await browser.create_context(url, proxy_server="http://host:port")

All three work. The next section is how to prove that for yourself, because the obvious check gives the wrong answer.

Verifying it applied

Point it at a port with nothing listening and load a page. If the proxy is in the path, you get Chrome's proxy error; if it is being ignored, you get the real site.

python
import asyncio, nodriver as uc

async def main():
    b = await uc.start(headless=True,
                       browser_args=["--proxy-server=http://127.0.0.1:9999"])
    p = await b.get("https://example.com")
    await asyncio.sleep(3)
    print(await p.evaluate("document.body.innerText.slice(0, 60)"))
    b.stop()

uc.loop().run_until_complete(main())

All three routes above give the same answer:

text
No internet
There is something wrong with the proxy server, or the address is incorrect

Without the proxy argument the same script prints Example Domain. So the argument takes effect, whichever way you pass it.

Read the page body, not the navigation result. This is the part worth keeping, and we got it wrong ourselves before checking properly: the navigation above succeeds. Chrome renders its own error page, the promise resolves, no exception is raised, and a script that only asks "did it load" concludes the proxy was skipped. The distinction is one line of JavaScript and it is the difference between a working check and a confident wrong answer.

The same trap appears with credentials rather than a dead port, and there it is worse: Chrome cannot parse a username and password in --proxy-server, drops the proxy entirely, and shows a generic "site can't be reached" that names neither. How to use an authenticated proxy in Selenium has the measurements.

Getting it to import on Python 3.14

nodriver 0.50.3 will not import on Python 3.14. It fails before your code runs:

text
SyntaxError: Non-UTF-8 code starting with '\xb1' on line 1345

One character in nodriver/cdp/network.py is stored as latin-1, and Python 3 reads source as UTF-8. Re-encode that one file:

python
from pathlib import Path
import nodriver, sys

f = Path(nodriver.__file__).parent / "cdp" / "network.py"
f.write_bytes(f.read_bytes().decode("latin-1").encode("utf-8"))

Run once, and it imports. It is a packaging bug rather than anything about how the library works, and it will presumably go away in a release.

What it reports when it runs

On Python 3.12, once running, it is respectable:

Signalnodriver
navigator.webdriverfalse
user agentHeadlessChrome/151
window.chromeobject
navigator.plugins.length5
launch time3.1 s

Three of four right, and it leaks HeadlessChrome in the user agent, which is the same gap Patchright has by default. It launches in 3.1 seconds against SeleniumBase UC mode's 10.4, which is the real trade between them: nodriver is fast and announces itself, SeleniumBase is clean and slow.

The full set is in how to choose an anti-detect browser.

Which field the credentials go in

Two provider-side things decide whether a working proxy argument actually connects. The session token sits in the username at some providers and the password at others, and the wrong field produces a 407 naming nothing. And some providers accept no username and password at all, wanting your IP on an allowlist. We publish what each provider states on its provider page, with the date we read it.

The proxy behind it is the recurring cost, and per-gigabyte prices vary more between providers than these tools vary between each other. What each charges, with the date it was read, is on the provider table.

Limitations

No anti-bot system and no evasion claim. Version 0.50.3, and the encoding fix above is a workaround for a packaging bug rather than a change to how the library works. The dead-port check is the durable part: it takes a minute and it answers the question for any tool, not just this one.

Sources

  1. nodriver PyPI, read 27 Aug 2026
    nodriver 0.50.3

Questions

Does nodriver support proxies?

The parameters exist and are accepted without error. In our test on 27 August 2026 at version 0.50.3 they did not take effect: a dead proxy on 127.0.0.1:9999 should stop a page load and did not, across browser_args, uc.Config and create_context. We do not know why and did not diagnose it.

Why does nodriver fail to import?

On Python 3.14.7 it raises a SyntaxError before your code runs, on a non-UTF-8 byte in its own vendored CDP source at network.py line 1345. The same version 0.50.3 imports and runs on Python 3.12.12, so the fix is an older interpreter rather than an older library.

How do I check whether my proxy is actually being used?

Point the tool at a port with nothing listening on it and load a page. If the page loads, the proxy is not being applied and your traffic is going out on your own address.