Authenticated proxies in Selenium: why every old recipe broke
If you have searched for “authenticated proxy in Selenium”, you almost certainly found the recipe that generates an extension on the fly. It does not work, and not for one reason: across 2025 Chrome removed four separate things, each of which breaks a different line of that recipe. Here is what broke, and what works now.
Four removals, not one
This is usually reduced to the death of Manifest V2, which is imprecise: for Selenium the bigger loss is elsewhere. Chrome 137 removed the --load-extension flag from branded builds — that flag is what loaded the generated extension, and its removal breaks the recipe regardless of manifest version. Chrome 138 was the last release where MV2 extensions could run at all. Chrome 139 removed the ExtensionManifestV2Availability policy along with the --disable-extensions-except and --extensions-on-chrome-urls flags. Chrome 142 removed even the temporary killswitch flag people used to get around all this.
MV3 can authenticate — this is not where the problem is
A common misconception holds that proxy authentication became impossible in Manifest V3. It did not. Blocking webRequest is indeed unavailable to most extensions in MV3, but onAuthRequired was carved out: it stays blocking if you request the webRequestAuthProvider permission. So the extension code is writable; the whole difficulty is loading it into branded Chrome.
// The minimum an extension needs for proxy auth under MV3
{
"manifest_version": 3,
"permissions": ["proxy", "webRequest", "webRequestAuthProvider", "storage"],
"host_permissions": ["<all_urls>"],
"background": { "service_worker": "background.js" }
}Why selenium-wire is no longer the answer
The library was archived on 3 January 2024 and remains frozen at 5.1.0 on PyPI. On modern Python it fails with the characteristic ModuleNotFoundError: No module named 'blinker._saferef', and the entire cause is two import lines in its vendored copy of mitmproxy: import blinker and import blinker._saferef. The private _saferef module was deleted in blinker 1.8.0, released 27 April 2024, while selenium-wire's metadata pins blinker with no upper bound.
# The stopgap if a project is already tied to selenium-wire:
# the last blinker release that still ships _saferef
pip install 'blinker==1.7.0'
# Living descendants (the names matter, both exist on PyPI):
pip install selenium-wire-2
pip install selenium-wire-lwBoth descendants are still man-in-the-middle proxies built on mitmproxy under the hood, so they bring a certificate and its overhead with them. A working route, but the most fragile one available.
What gets recommended and does not work
- Proxy(socks_username=..., socks_password=...) — a leftover of the old protocol: those keys are not in the W3C capability table, and the specification requires an error for unrecognised keys.
- driver.register(login, password) — no such method exists in current Selenium for Python. A hasattr check returns True only because the method is inherited from abc.ABCMeta.
- Credentials inside the proxy address — Chrome ignores them, by its own documentation.
- The BiDi handler driver.network.add_auth_handler — the method exists, but proxy authentication through it does not work in Chrome: that is an open, confirmed bug.
Four routes that do work
- Do not authenticate in the browser at all: run a local password-free proxy that adds the credentials upstream. In mitmproxy that is upstream mode plus the upstream_auth option. For Chrome it is also the only way to use an authenticated SOCKS5 proxy.
- CDP: enable Fetch with auth handling and answer the authRequired event with continueWithAuth. The protocol covers proxies explicitly — the challenge source distinguishes Server from Proxy.
- An MV3 extension installed not by flag but over BiDi: Selenium can install extensions with the webextension.install command, and Chrome additionally needs the extension-debugging launch switches and remote debugging over a pipe.
- Sidestep branded Chrome: in Chromium and Chrome for Testing the --load-extension flag still works, so the classic recipe survives there — if you port the manifest to version 3.
Firefox is simpler
Firefox's WebExtension API has a proxyAuthorizationHeader field that is passed straight into the Proxy-Authorization header on CONNECT. No challenge-and-response dance is needed; the value goes in directly. One limitation: HTTP and HTTPS proxies only, not SOCKS.
Or do not authenticate at all
All four routes above exist for one purpose: to hand a password to a browser that does not want it. There is a shorter way out — do not hand it over. With IP authentication the machine's address goes on a whitelist, the proxy recognises the client by the connection, and only host and port remain in the settings. For SOCKS5 in Chrome it is the only way without a local relay, because Chromium does not support SOCKS5 authentication at all.
Both methods are active at once here, so nothing has to be switched: the machine running your tests works on the whitelist while everything else keeps using login and password. Checked against Selenium 4.47 and Chrome 152.
A whitelist instead of fighting the browser
IP and login authentication run in parallel on every plan. Rotating IPv6 from 650 ₽ for 50 threads, IPv4 from 1375 ₽ for 100, static IPv4 from 300 ₽.
View pricingProxies for this job
Check it with our tools
Read next
- The program will not take a proxy login and password: which clients cannot send them, and what to doChromium has never supported SOCKS5 authentication, Android has no password field, netsh has no parameter. A breakdown by client, a one-command check, and the whitelist route around it.
- Proxies in Playwright and Puppeteer: setup, authentication and per-context IPsWhy credentials in the connection string fail in Chromium, how to give every context its own address, and how that relates to your thread limit.
- Automating proxies over the API: whitelist, purchase and renewal from a scriptA PUT to the whitelist replaces the whole list rather than adding to it — that is how people lock themselves out. A walkthrough of our API: the key and its limits, updating an address from cron, purchase and renewal, error codes.
