Guide

Proxies for undetected-chromedriver: Setup, Authentication and SOCKS5 (Measured on Chrome 151)

undetected-chromedriver proxy setup: --proxy-server, why user:pass in the URL fails, three fixes that work, SOCKS5 and headless, measured on Chrome 151.

HProxy Team··Updated September 2, 2026·19 min read
HProxy.Guide

Free proxies won't hold up here.

Shared datacenter IPs get flagged and dropped fast. When it has to hold, gaming, streaming, accounts, you need mobile and residential IPs that read as a real device, from $0.44/GB, pay as you go.

See plans & pricing

undetected-chromedriver (uc) takes a proxy the way Chrome does, through the --proxy-server switch on its ChromeOptions. Pin version_main to the major version of your installed Chrome, and the whole setup is seven lines.

import undetected_chromedriver as uc

options = uc.ChromeOptions()
options.add_argument("--proxy-server=http://203.0.113.10:8080")   # host:port, no credentials
driver = uc.Chrome(options=options, version_main=151)             # your Chrome major version
driver.get("https://httpbin.org/ip")
print(driver.find_element("tag name", "body").text)
driver.quit()

We ran this on 2 September 2026 with undetected-chromedriver 3.5.5, Selenium 4.48.0 and Chrome 151 on Windows 11. Through a public entry from our free proxy list, httpbin.org answered with the address of the proxy 0.42 seconds after driver.get. The figure shows what the same flag does once the proxy wants a password, and the fix that got through.

Three captures of our uc-driven Chrome: ERR_NO_SUPPORTED_PROXIES with credentials in the flag, the Sign in dialog without them, and the forwarding helper showing a public exit address.
Captured on our own machine on 2 September 2026: credentials inside --proxy-server, the proxy sign-in dialog, and the local forwarding helper with a public free-list exit.

A username and password inside that URL do not work, and that is the reason most searches on this topic exist. Chrome refuses the address outright. Without credentials it opens a sign-in dialog that no script can fill. This page measures the flag, the failure and every fix people recommend, on the current versions. Three fixes work today. Four do not, and the page shows the exact error for each.

What you need before you start

  • Python 3.10 or newer, because Selenium 4.48.0 lists 3.10 as its floor on PyPI (August 2026).
  • Google Chrome installed. uc drives the installed browser and downloads a chromedriver for it.
  • The packages: pip install undetected-chromedriver setuptools. The second one matters on Python 3.12 and newer, and the next section says why.
  • The major version of your Chrome, from chrome://version. It goes into version_main.
  • A proxy address without credentials for the first test. Any HTTP entry from our free proxy list will do.
componentversion measuredsource
undetected-chromedriver3.5.5PyPI, released 17 February 2024, still the latest.
selenium4.48.0PyPI, released 27 August 2026.
Google Chrome151.0.7922.174installed on the test machine.
chromedriver151.0.7922.138downloaded and patched by uc after the version pin.
Python3.13.7Windows 11.

Why does the first launch fail before any proxy is involved?

A fresh install produced two errors before the proxy flag was even read. Both have one-line fixes.

Error 1: No module named 'distutils'

  File "...\undetected_chromedriver\patcher.py", line 4, in <module>
    from distutils.version import LooseVersion
ModuleNotFoundError: No module named 'distutils'

Python 3.12 removed the distutils package. The release notes (October 2023) say that "the third-party Setuptools package continues to provide distutils". uc 3.5.5 still imports it, so install setuptools next to uc and the import works. The fix on the master branch of the repository dates from July 2025 and never reached PyPI, where the last release is from February 2024. Installing from git is the other option, and the README documents it.

Error 2: This version of ChromeDriver only supports Chrome version 152

selenium.common.exceptions.SessionNotCreatedException: Message: session not created: cannot connect to chrome at 127.0.0.1:54611
from session not created: This version of ChromeDriver only supports Chrome version 152
Current browser version is 151.0.7922.174

Without a pin, uc downloaded the newest chromedriver, one major version ahead of the installed browser. Pass version_main=151, or whatever chrome://version shows, and uc fetches the matching driver. Our first pinned launch took 6.47 seconds, including the download and the patch. Later launches took 3.6 to 4.2 seconds.

The message at exit

On Windows, uc prints OSError: [WinError 6] from its __del__ method after quit() has already closed the browser. That line is noise. The browser is gone and the temporary profile is deleted.

How do I set an HTTP proxy in undetected-chromedriver?

Step 1: get an address that is alive right now

Public entries die fast, so pull a fresh one at run time instead of pasting one into the script. Our free proxy API returns the current list as plain text.

import requests

pool = requests.get(
    "https://hproxy.com/api/proxy-list",
    params={"format": "txt", "protocol": "http", "recent": "true", "limit": 40},
    timeout=15,
).text.split()

We scanned that list three times on 2 September 2026 by sending one request through each entry. Alive were 22 of 60 entries at 14:04 UTC, 11 of 40 at 14:12, and 3 of 40 at 20:08. Test each entry with a plain HTTP client first. A browser launch costs about four seconds, and a dead entry wastes all of them.

Step 2: pass it with --proxy-server

options = uc.ChromeOptions()
options.add_argument(f"--proxy-server=http://{pool[0]}")
driver = uc.Chrome(options=options, version_main=151)

The Chromium proxy documentation defines the switch. Three rules follow from it and from our runs.

rulewhat it means for uc
One value for the whole browserEvery tab and every request of that Chrome uses the proxy, including the background requests Chrome makes on its own.
Read once at launchChanging the proxy means quitting and starting a new driver.
Host and port onlyCredentials in the value are refused, as the next section shows.

Accepted schemes are http://, https:// (a proxy that itself speaks TLS), socks4:// and socks5://. A bare host:port means HTTP.

Step 3: check what the target sees

Never trust the flag. Compare the address the target reports with and without the proxy.

import requests
import undetected_chromedriver as uc

direct = requests.get("https://httpbin.org/ip", timeout=15).json()["origin"]

options = uc.ChromeOptions()
options.add_argument("--proxy-server=http://203.0.113.10:8080")
driver = uc.Chrome(options=options, version_main=151)
driver.get("https://httpbin.org/ip")
via = driver.find_element("tag name", "body").text
driver.quit()

print("direct: ", direct)
print("browser:", via)
assert direct not in via, "the browser is not using the proxy"

In our run through the entry 91.134.141.4:3128, the browser reported exactly that address. The first load took 0.42 seconds and the second 0.13 seconds. Our own free proxy list page loaded through it in 0.62 seconds. If the browser shows your own address, the flag did not take. A Stack Overflow question from 2022 shows that outcome, and the check above catches it before the run. Before you trust an entry, drop it into our proxy checker to see its exit address, anonymity and speed from the outside.

Why does a username and password in --proxy-server not work?

Because Chrome ignores them by design. The Chromium proxy documentation says: "Chrome does not implement this, and will not use any credentials embedded in the proxy settings." We measured the three ways of writing the flag against a local proxy that requires Basic authentication.

what you passwhat Chrome 151 didmeasured
http://user:pass@host:port, headed or headlessThe error page ERR_NO_SUPPORTED_PROXIES. No request reached the proxy at all.0.10 s.
http://host:port, headedThe proxy answered 407 and Chrome opened the dialog "Sign in. The proxy http://127.0.0.1:18898 requires a username and password." driver.get returned with an empty page.0.09 s.
http://host:port, headlessNo dialog and no error page, only a blank document, still blank two seconds later.0.08 s.

The Chromium error list defines ERR_NO_SUPPORTED_PROXIES as "There are no supported proxies in the provided list". A value with credentials is not a proxy Chrome knows how to use, so it never connects. The 407 comes from RFC 9110, which requires the client to "authenticate itself in order to use a proxy for this request". Chrome supports Basic, Digest, Negotiate and NTLM for proxies, and for Basic it asks a person through that dialog. Selenium cannot type into the dialog, so the script waits.

What works for an authenticated proxy today?

We measured every approach the ranking tutorials and the forum threads recommend, on 2 September 2026.

approachresultwhere
Credentials in --proxy-serverERR_NO_SUPPORTED_PROXIES.previous section.
Local forwarding helper, 60 lines of PythonWorks, 0.49 s first load, no dialog.fix 1.
proxy.py as that helper, no codeWorks, 0.54 s.fix 2.
Second DevTools connection answering Fetch.authRequiredWorks headed and headless, 0.46 s.fix 3.
uc enable_cdp_events with add_cdp_listenerNever receives the event; our runs hung and were killed.below.
Selenium BiDi network.add_auth_handlerInvalid InterceptionId, timeout after 20 s.below.
Manifest V3 extension via --load-extensionIgnored on branded Chrome 151; nothing loaded.below.
selenium-wireFour version pins to start, then NET::ERR_CERT_AUTHORITY_INVALID.below.
IP allowlisting at the providerNo code needed; not measured here.your provider.

Fix 1: a local forwarding helper

Chrome talks to a proxy on 127.0.0.1 that needs no password. That helper adds the Proxy-Authorization header of RFC 9110 on the way to the real proxy and pipes bytes in both directions. Save this as forwarder.py.

import asyncio, base64, sys

def parse(target):
    creds, _, hostport = target.rpartition("@")
    host, _, port = hostport.rpartition(":")
    return creds, host, int(port)

async def pipe(reader, writer):
    try:
        while True:
            data = await reader.read(65536)
            if not data:
                break
            writer.write(data)
            await writer.drain()
    except (ConnectionError, asyncio.CancelledError):
        pass
    finally:
        try:
            writer.close()
        except Exception:
            pass

async def handle(client_reader, client_writer, upstream, auth_header):
    head = await client_reader.readuntil(b"\r\n\r\n")
    lines = head.decode("latin-1").split("\r\n")
    request_line = lines[0]
    headers = [l for l in lines[1:] if l and not l.lower().startswith("proxy-authorization:")]
    headers.append(auth_header)
    new_head = "\r\n".join([request_line, *headers]) + "\r\n\r\n"
    try:
        up_reader, up_writer = await asyncio.open_connection(*upstream)
    except OSError:
        client_writer.write(b"HTTP/1.1 502 Bad Gateway\r\nContent-Length: 0\r\n\r\n")
        await client_writer.drain()
        client_writer.close()
        return
    up_writer.write(new_head.encode("latin-1"))
    await up_writer.drain()
    await asyncio.gather(pipe(client_reader, up_writer), pipe(up_reader, client_writer))

async def main():
    creds, host, port = parse(sys.argv[1])
    listen_port = int(sys.argv[2]) if len(sys.argv) > 2 else 8899
    auth_header = "Proxy-Authorization: Basic " + base64.b64encode(creds.encode()).decode()
    server = await asyncio.start_server(
        lambda r, w: handle(r, w, (host, port), auth_header), "127.0.0.1", listen_port)
    print(f"forwarding 127.0.0.1:{listen_port} -> {host}:{port} with Basic auth", flush=True)
    async with server:
        await server.serve_forever()

if __name__ == "__main__":
    asyncio.run(main())

Run it with the real proxy, then point uc at the helper.

python forwarder.py user:pass@proxy.example.net:8080 8899
options = uc.ChromeOptions()
options.add_argument("--proxy-server=http://127.0.0.1:8899")
driver = uc.Chrome(options=options, version_main=151)

Measured: httpbin.org/ip loaded in 0.49 seconds, then 0.20 seconds, and a plain http:// target in 0.31 seconds. The authenticating proxy logged 40 requests in that one session, every one with valid credentials and none answered 407. Most of them were not ours. Chrome contacted 24 hosts in a session of about ten seconds, and the update, web store and telemetry hosts of the browser itself made up the bulk. The helper handles CONNECT tunnels for https targets and plain-http requests alike. It does not speak SOCKS.

Fix 2: proxy.py as the helper

pip install proxy.py gives you the same helper as one command. Its pool plugin takes the upstream proxy with credentials.

proxy --port 8899 --plugins proxy.plugin.ProxyPoolPlugin --proxy-pool user:pass@proxy.example.net:8080

Point uc at http://127.0.0.1:8899 as above. In our run a plain request through it came back 200, and the upstream proxy logged the credentials as valid. uc then loaded httpbin.org/ip in 0.54 seconds. The version measured is proxy.py 2.4.10 (PyPI, February 2025).

Fix 3: answer the challenge over the DevTools protocol

The Chrome DevTools Protocol has a Fetch domain built for this. Fetch.enable with handleAuthRequests pauses every request. Fetch.authRequired carries the challenge, and Fetch.continueWithAuth sends the credentials. uc exposes the debugger address, so a second websocket to the page target can run that loop while Selenium drives the page. Save this as cdp_proxy_auth.py; it needs only websocket-client, which Selenium already installs.

import json, threading
import requests
import websocket


class CdpProxyAuth(threading.Thread):
    def __init__(self, driver, username, password):
        super().__init__(daemon=True)
        self.username, self.password = username, password
        addr = driver.capabilities["goog:chromeOptions"]["debuggerAddress"]
        pages = requests.get(f"http://{addr}/json", timeout=5).json()
        self.ws_url = next(p["webSocketDebuggerUrl"] for p in pages if p["type"] == "page")
        self.ws = websocket.create_connection(self.ws_url, suppress_origin=True)
        self._id = 0
        self.seen = []

    def send(self, method, params=None):
        self._id += 1
        self.ws.send(json.dumps({"id": self._id, "method": method, "params": params or {}}))

    def start(self):
        self.send("Fetch.enable", {"handleAuthRequests": True, "patterns": [{"urlPattern": "*"}]})
        super().start()

    def run(self):
        while True:
            try:
                msg = json.loads(self.ws.recv())
            except Exception:
                return
            method = msg.get("method")
            if method == "Fetch.requestPaused":
                self.send("Fetch.continueRequest", {"requestId": msg["params"]["requestId"]})
            elif method == "Fetch.authRequired":
                challenge = msg["params"]["authChallenge"]
                self.seen.append(challenge)
                self.send("Fetch.continueWithAuth", {
                    "requestId": msg["params"]["requestId"],
                    "authChallengeResponse": {"response": "ProvideCredentials",
                                              "username": self.username, "password": self.password}})

    def stop(self):
        try:
            self.send("Fetch.disable")
            self.ws.close()
        except Exception:
            pass
from cdp_proxy_auth import CdpProxyAuth

driver = uc.Chrome(options=options, version_main=151)   # options carry --proxy-server=http://host:port
auth = CdpProxyAuth(driver, "user", "pass")
auth.start()
driver.get("https://httpbin.org/ip")
auth.stop()

Measured: the challenge arrived once per run as source: Proxy, scheme: basic, and httpbin.org/ip loaded in 0.46 seconds headed and 0.44 seconds headless. Two limits apply. Fetch is enabled per page target, so the requests of the browser outside that tab still get 407, which is harmless. A new tab needs its own session.

Why not enable_cdp_events? uc offers uc.Chrome(enable_cdp_events=True) and driver.add_cdp_listener, and several answers use them for authentication. They cannot work here. The listener reads the performance log of chromedriver, and chromedriver only writes Network. and Page. events into that log; the file is performance_logger.cc. A Fetch.authRequired event never arrives, the paused requests are never continued, and our two runs never returned. We killed them after 150 seconds and after ten minutes.

What does not work anymore

The proxy-auth extension. For years the answer was a small extension: chrome.proxy.settings.set for the address and chrome.webRequest.onAuthRequired for the credentials. Both APIs still exist (webRequest, proxy), and Manifest V3 has the webRequestAuthProvider permission for exactly this. The way in is gone. Since Chrome 137 the --load-extension switch is removed in official Chrome builds. The announcement (Chrome Counter Abuse, 4 April 2025) keeps it for Chromium and Chrome for Testing. We built a Manifest V3 version and loaded it three ways on Chrome 151: plain, with --disable-features=DisableLoadExtensionCommandLineSwitch, and headless. In every case the browser reported our own address and the proxy saw no request. The add_extension call of Selenium rides on the same switch. chromedriver unpacks the crx and passes --load-extension itself, per chrome_launcher.cc.

selenium-wire. The library that every 2022 to 2024 tutorial recommends is archived, and its README says it "is no longer being maintained". Its last release, 5.1.0, is from October 2022. On current dependencies the import fails with ModuleNotFoundError: No module named 'blinker._saferef', on Python 3.13 and 3.10 alike. With blinker==1.7.0 it imports on 3.10, but its bundled mitmproxy crashes with AttributeError: 'X509' object has no attribute 'get_extension' and the page shows ERR_CONNECTION_CLOSED. Python 3.13 also needs a setuptools older than 81 for pkg_resources. With pyOpenSSL 23.3.0 and cryptography 41.0.7 as well, it started on both Pythons and sent the credentials. Chrome then answered with NET::ERR_CERT_AUTHORITY_INVALID, because selenium-wire terminates TLS with its own certificate. Four pins for a browser that then has to ignore certificate errors. Use fix 1 instead.

The BiDi authentication handler of Selenium. Selenium 4.48 has options.enable_bidi = True and driver.network.add_auth_handler(user, password). Against our proxy the navigation timed out after 20 seconds, and the handler thread raised WebDriverException: unknown error: Invalid InterceptionId., with uc and with plain Selenium. A control run against the 401 of a site (httpbin.org/basic-auth) failed the same way. The handler did not work on this Selenium and Chrome pair at all.

The successors, for completeness. The author of uc moved on to nodriver, which its documentation calls "the official successor" and which lists authenticated SOCKS5 proxies as a feature. SeleniumBase takes a proxy with credentials as an argument in its UC Mode, and its documentation warns that UC Mode is detectable in headless mode. We did not measure either.

Does SOCKS5 work with undetected-chromedriver?

Yes, without authentication. We measured it against our own SOCKS5 server on 127.0.0.1, because 0 of 40 public socks5 entries on our list were alive that day.

HTTP proxySOCKS5 proxy
flag--proxy-server=http://host:port--proxy-server=socks5://host:port
authentication in ChromeBasic, Digest, Negotiate, NTLM, through a challengenone; Chrome offers only the no-authentication method
who resolves the hostnamethe proxy, from the CONNECT linethe proxy; Chrome sent httpbin.org as a domain name
credentials in the URLERR_NO_SUPPORTED_PROXIESERR_NO_SUPPORTED_PROXIES
proxy demands a passwordsign-in dialog headed, blank page headlessERR_SOCKS_CONNECTION_FAILED, raised by driver.get after 0.13 s
public entries alive on our list, 2 September 202622 of 60, 11 of 40, 3 of 400 of 40, twice

The no-auth run loaded httpbin.org/ip in 0.44 seconds. The server log showed the browser offering method 0 only and sending the target as a domain name, address type 3. That matches the Chromium document: with SOCKSv5 "name resolution is always done proxy side" and "No authentication methods are supported for SOCKSv5 in Chrome". A SOCKS5 proxy with a username and password therefore needs a helper as well, and the HTTP forwarder above does not speak SOCKS. Ask the provider for its HTTP port, or allowlist your IP on the SOCKS port. Of the 40 dead public entries, 34 failed the TLS certificate check rather than staying silent; our SOCKS5 error guide explains what that looks like.

Does the proxy still work in headless mode?

Yes. uc.Chrome(headless=True) passes --headless=new on Chrome 108 and newer, and through the public entry the headless browser reported the address of the proxy in 0.39 seconds. Two things to know. First, navigator.userAgent still read HeadlessChrome/151.0.0.0 in our run, so the mode is visible to a page that looks. Second, there is no dialog in headless mode, so an authenticating proxy leaves a blank page with no error text at all. Use fix 1, 2 or 3; fix 3 was measured headless. The Chrome headless documentation (October 2024) describes the new mode as the full browser without a window.

How do I keep some hosts off the proxy?

--proxy-bypass-list takes "a semicolon or comma separated list of bypass rules", per the Chromium document. We combined it with the helper chain.

options.add_argument("--proxy-server=http://127.0.0.1:8899")
options.add_argument("--proxy-bypass-list=httpbin.org;localhost")

httpbin.org then reported our own address in 0.41 seconds. The proxy log recorded every other host: our checker page and the Google hosts Chrome contacts at start. Budget for that background traffic when a proxy bills by the megabyte. A fresh uc profile made 40 requests to 24 hosts in one short session in our measurement.

Should proxies rotate per request or per session?

Per session, because the browser cannot do anything else. The flag is read once at launch, and every new proxy means a new driver. Our launches cost 3.6 to 4.2 seconds each, so rotating on every request is a bad trade. Rotate per task instead: build the driver, do the work of one identity, quit, next entry.

for entry in pool:
    options = uc.ChromeOptions()
    options.add_argument(f"--proxy-server=http://{entry}")
    driver = uc.Chrome(options=options, version_main=151)
    try:
        driver.get("https://httpbin.org/ip")
        # the work of this identity
    finally:
        driver.quit()

Switching the exit in the middle of a page would also split one page across two addresses, which our proxies for Selenium page covers. The alternative is one endpoint that rotates on its side. Our residential pool works that way: a single host and port in the flag, and a different exit per session. Put the helper from fix 1 in front of it, because the pool authenticates with a username and password. The code on this page does not change.

What do the errors mean?

Every measured line below comes from our runs on 2 September 2026. The two tunnel errors are quoted from the Chromium error list, because they did not occur in our runs.

what you seewhyfix
ModuleNotFoundError: No module named 'distutils'Python 3.12 and newer removed it; uc 3.5.5 imports itpip install setuptools.
SessionNotCreatedException: ... only supports Chrome version 152uc fetched the newest driverversion_main=<your major>.
ERR_NO_SUPPORTED_PROXIEScredentials inside the flag, http or socks5host:port only, then fix 1, 2 or 3.
sign-in dialog, page emptythe proxy answered 407, headedfix 1, 2 or 3.
blank page, no error, headlessthe proxy answered 407, no dialog possiblefix 1, 2 or 3.
ERR_SOCKS_CONNECTION_FAILEDthe SOCKS5 proxy wants a password Chrome cannot sendHTTP port with a helper, or IP allowlisting.
ERR_PROXY_CONNECTION_FAILEDChrome could not reach the proxy at alldead entry or wrong port, next entry.
ERR_TUNNEL_CONNECTION_FAILEDthe proxy refused the CONNECT tunnelthe entry does not carry HTTPS, next entry.
TimeoutException after 20 s, Invalid InterceptionId in a threadthe BiDi auth handler on Selenium 4.48 and Chrome 151fix 3.
the script never returns after Fetch.enablethe uc listener cannot see Fetch eventsfix 3.
No module named 'blinker._saferef' or 'pkg_resources'selenium-wire on current blinker and setuptoolsfix 1.
NET::ERR_CERT_AUTHORITY_INVALIDselenium-wire re-signs TLSfix 1.
OSError: [WinError 6] at exituc __del__ on Windows after quit()ignore.
the browser shows your own addressthe flag did not apply, or the bypass list covers the hostStep 3.

The net:: family in headless frameworks has its own page, ERR_PROXY_CONNECTION_FAILED in Puppeteer, Playwright and Selenium. The challenge itself is covered in how to fix 407 Proxy Authentication Required.

How does this differ from plain Selenium?

On the proxy path, barely. We ran plain Selenium 4.48.0 with the same flag and the same public entry in the same minute.

undetected-chromedriver 3.5.5Selenium 4.48.0
--proxy-serverworks, exit equal to the proxyworks, exit equal to the proxy
credentials in the flagrefused by Chrome, before any driver is involvednot measured separately; the switch belongs to Chrome
navigator.webdriverfalsetrue
driver binarydownloaded and patched by uc, needs version_mainSelenium Manager, automatic
start-up in our run3.75 s2.18 s
headless user agentHeadlessChrome/151.0.0.0not measured
BiDi add_auth_handlerfails, Invalid InterceptionIdfails, the same error
extension via --load-extensionignored on branded Chrome 137 and newerthe same switch, per chromedriver source

A proxy changes the address a site sees and nothing else. The README of uc states it in capitals: the package does not hide your IP address, and a datacenter address is likely to fail. Fingerprint and behaviour are outside this page. Our proxies for Selenium page covers which proxy type fits which target. The Selenium proxy tutorial is the sibling of this page for plain Chrome and Firefox.

Where to go from here

Free proxies for Selenium covers what a public entry can and cannot carry in a browser. Pull entries from the free proxy list at run time and let the loop discard the dead ones. For production, our residential proxies give one endpoint and fresh exits, and the API documentation covers key creation and plan generation without a dashboard.

Sources

  • undetected-chromedriver README (ultrafunkamsterdam, GitHub; README last changed 12 June 2023, repository last commit 5 July 2025). What the package does, the IP statement, the git install line, the issue tracker notice.
  • Release histories of undetected-chromedriver, selenium, selenium-wire, proxy.py and nodriver (PyPI, read 2 September 2026).
  • Proxy support in Chrome, net/docs/proxy.md (The Chromium Authors, read at HEAD on 2 September 2026). --proxy-server, --proxy-bypass-list, embedded credentials, SOCKSv5.
  • net/base/net_error_list.h (The Chromium Authors, read at HEAD on 2 September 2026). Error definitions.
  • chromedriver chrome_launcher.cc and performance_logger.cc (The Chromium Authors, read at HEAD on 2 September 2026). Extension loading; performance log domains.
  • PSA: Removing --load-extension flag in Chrome branded builds (Richard Chen, Chrome Counter Abuse, chromium-extensions group, 4 April 2025).
  • chrome.webRequest and chrome.proxy API references (Chrome for Developers, read 2 September 2026).
  • Chrome DevTools Protocol, Fetch domain (tip-of-tree, read 2 September 2026).
  • RFC 9110, HTTP Semantics (IETF, June 2022). Sections 11.7 and 15.5.8.
  • Selenium documentation, Browser options and Chrome-specific functionality (Selenium project, read 2 September 2026); selenium 4.48.0 source, bidi/network.py.
  • selenium-wire README (Will Keeling, GitHub, repository archived; last release October 2022).
  • What's New In Python 3.12 (Python Software Foundation, October 2023). The distutils removal.
  • blinker CHANGES, version 1.8.0 (Pallets Eco, 27 April 2024).
  • Chrome Headless mode (Chrome for Developers, updated 21 October 2024).
  • nodriver documentation (ultrafunkamsterdam, read 2 September 2026) and SeleniumBase UC Mode documentation (SeleniumBase, read 2 September 2026).
  • Our own measurements on 2 September 2026: undetected-chromedriver 3.5.5, Selenium 4.48.0, Chrome 151.0.7922.174, Python 3.13.7 and 3.10.11 on Windows 11, against public entries from hproxy.com/free-proxy-list and our own proxies on 127.0.0.1.

Frequently asked questions

How do I add a proxy with a username and password to undetected-chromedriver?
Not through the flag. Chrome refuses a --proxy-server value that contains credentials with ERR_NO_SUPPORTED_PROXIES. Run a small forwarding proxy on 127.0.0.1 that adds the Proxy-Authorization header, either 60 lines of Python or proxy.py with --proxy-pool user:pass@host:port, and point --proxy-server at it. A second DevTools connection that answers Fetch.authRequired works as well.
Why does undetected-chromedriver still show my own IP address with a proxy set?
Either the flag never reached Chrome, the value was refused, or the host is on the bypass list. Verify with httpbin.org/ip once directly and once in the browser: the two addresses must differ. Check that the argument reads exactly --proxy-server=http://host:port and that the options object is passed as options=.
Can I change the proxy while the browser is running?
No. Chrome reads --proxy-server once at launch, and undetected-chromedriver has no call to replace it. Quit the driver and start a new one with the next address; a launch cost 3.6 to 4.2 seconds in our measurement. A rotating gateway that changes the exit on its own side avoids the relaunch.
Does the proxy work in headless mode?
Yes. With headless=True the browser reported the proxy address the same way in our run. An authenticating proxy is the exception: there is no dialog in headless mode, so the page stays blank without an error. Use the forwarding helper or the DevTools fix, which need no window.
Does selenium-wire still work with undetected-chromedriver?
Not on current dependencies. selenium-wire is archived, and its import fails with No module named blinker._saferef. Pinning blinker 1.7.0, an older setuptools and pyOpenSSL 23 got it started in our test, and Chrome then showed NET::ERR_CERT_AUTHORITY_INVALID because selenium-wire re-signs TLS. A local forwarding helper replaces it.
Does undetected-chromedriver work with SOCKS5 proxies?
Yes, with --proxy-server=socks5://host:port, and Chrome lets the proxy resolve hostnames. Chrome supports no SOCKS5 authentication, so a SOCKS5 proxy that requires a username and password fails with ERR_SOCKS_CONNECTION_FAILED. Use the HTTP port of the provider with the forwarding helper, or allowlist your IP.
Does a proxy make undetected-chromedriver undetectable?
No. A proxy changes the address a site sees and nothing else. The README of the package states that it does not hide your IP and that a datacenter address is likely to fail. Fingerprint and behaviour are separate layers, and a residential exit helps the address layer only.

Proxies that don't die mid-job

Residential, ISP, datacenter and mobile, verified by the same engine that runs tens of millions of checks. They read as a real device and hold up under load. Pay as you go, and your balance never expires. $0.44/GB is the 2,000 GB+ rate; a single gigabyte is $0.50/GB, with no minimum order.

129M+ proxy checks run · 100+ countries · HTTP / HTTPS / SOCKS · re-checked every few minutes · no signup