Selenium Proxy with ChromeDriver and Python: Authentication That Works in 2026

Developer guide Β· Last reviewed 28 September 2026 Β· 13 min read

Setting up a Selenium proxy looks like a one-line job until the proxy asks for a username and password. Chrome has no command-line flag for proxy credentials, the popular selenium-wire package was archived in January 2024, and since Chrome 137 the old trick of loading a small authentication extension no longer works in regular Chrome. This guide shows what does work today with ChromeDriver and Python: the --proxy-server flag, Selenium’s own authentication handler, the extension method in Chrome for Testing, Firefox, rotation, and fixes for the common errors, with code you can copy.

The short version

Point Chrome at the proxy with --proxy-server=http://v2.proxyempire.io:5000, turn on WebDriver BiDi with options.enable_bidi = True, and register your credentials with driver.network.add_auth_handler(user, password) before the first page load. Use an HTTP proxy endpoint for authenticated sessions, because Chrome cannot send a username and password to a SOCKS5 proxy, and start a new driver when you need a clean IP.

How a Selenium Proxy Works with ChromeDriver

The browser connects through the proxy, not the driver

Selenium does not send web traffic itself. Your Python script talks to ChromeDriver, ChromeDriver starts and controls Chrome, and Chrome loads the pages. So a proxy for Selenium is really a proxy for the browser: every page, script, image and API call the browser makes goes through it, while the local connection between your script and ChromeDriver stays direct. That is why the proxy settings live in the browser options you hand to the driver, and why anything the browser supports, or does not support, applies to your automation too.

Three pieces of information describe the proxy: the host and port of the gateway, the protocol (HTTP or SOCKS5), and the credentials. With ProxyEmpire the gateway is v2.proxyempire.io:5000, the credentials come from your dashboard, and the username can also carry location and session options, so one endpoint gives you any country or a sticky IP without changing code. The table below shows which part is easy and which is not.

NeedChrome optionWorks in Selenium today?
Proxy host and port--proxy-server or the proxy capabilityYes, in every browser build
Username and password (HTTP proxy)No flag existsYes, with the BiDi auth handler or an extension in Chrome for Testing
Username and password (SOCKS5 proxy)No flag existsNo, Chrome does not support SOCKS5 authentication
Different proxy per browserOne driver per proxyYes, start separate drivers

If you only need an unauthenticated proxy, the next section is all you need. If your provider asks for a login, which almost every commercial residential or mobile proxy does, read the authentication section as well. New to automated browsers in general? Our headless browser guide compares Selenium with Playwright and Puppeteer.

Set a Chrome Proxy in Selenium Without a Password

One argument or one capability

The quickest way to set a ChromeDriver proxy is Chrome’s own command-line switch. It takes a scheme, host and port, and applies to every request the browser makes.

Python Β· –proxy-server flag
from selenium import webdriver
from selenium.webdriver.common.by import By

options = webdriver.ChromeOptions()
options.add_argument("--proxy-server=http://v2.proxyempire.io:5000")
options.add_argument("--headless")   # optional

driver = webdriver.Chrome(options=options)
driver.get("https://api.ipify.org?format=json")
print(driver.find_element(By.TAG_NAME, "body").text)
driver.quit()

Selenium also has a browser-neutral way to say the same thing: the proxy capability defined by the WebDriver standard. It is useful when the same test suite runs on Chrome, Edge and Firefox, or on a Selenium Grid where you would rather not pass browser-specific flags.

Python Β· proxy capability
from selenium import webdriver
from selenium.webdriver.common.proxy import Proxy, ProxyType

proxy = Proxy({
    "proxyType": ProxyType.MANUAL,
    "httpProxy": "v2.proxyempire.io:5000",
    "sslProxy":  "v2.proxyempire.io:5000",
})

options = webdriver.ChromeOptions()
options.proxy = proxy
driver = webdriver.Chrome(options=options)

Two details catch people out. First, do not put a username and password in the flag, as in http://user:pass@host:port: Chrome ignores the credentials and then shows a sign-in prompt, which a script cannot answer. Second, since Selenium 4.6 you do not need to download ChromeDriver by hand; Selenium Manager finds or fetches the right driver for the installed browser. That download uses your normal connection, not the proxy, so it does not count against your proxy traffic.

If the proxy recognises your machine’s IP address instead of a password, the code above is complete. For everything else, keep reading. The same flag works for a normal Chrome window too; our guide to Chrome proxy settings covers manual setup on Windows and macOS.

Selenium Proxy Authentication with the BiDi Auth Handler

The supported way to send a username and password

Recent versions of Selenium can answer the browser’s authentication challenge for you. The feature runs on WebDriver BiDi, the newer two-way protocol that lets your script listen to browser events while the page loads. When the proxy replies with HTTP 407 and asks for credentials, the browser raises an “auth required” event, and a handler registered by your script supplies the username and password. No extension, no man-in-the-middle proxy and no sign-in prompt.

Python Β· Selenium 4.34+ with BiDi
from selenium import webdriver
from selenium.webdriver.common.by import By

PROXY = "http://v2.proxyempire.io:5000"
USERNAME = "YOUR_USERNAME"   # from the ProxyEmpire dashboard
PASSWORD = "YOUR_PASSWORD"

options = webdriver.ChromeOptions()
options.enable_bidi = True
options.add_argument(f"--proxy-server={PROXY}")

driver = webdriver.Chrome(options=options)
handler_id = driver.network.add_auth_handler(USERNAME, PASSWORD)

try:
    driver.get("https://api.ipify.org?format=json")
    print("Exit IP:", driver.find_element(By.TAG_NAME, "body").text)
finally:
    driver.network.remove_auth_handler(handler_id)
    driver.quit()

Follow a few rules and it behaves predictably:

  • Upgrade Selenium first. The Python add_auth_handler method is part of Selenium’s BiDi network module; run pip install -U selenium and use a current Chrome.
  • Enable BiDi before creating the driver. options.enable_bidi = True has to be set on the options you pass in; turning it on later does nothing.
  • Register the handler before the first driver.get(). The browser asks for credentials on its first proxied request; if nobody answers, the page fails.
  • Use the HTTP endpoint. The handler answers HTTP proxy challenges. Chrome never asks for SOCKS5 credentials, so an authenticated SOCKS5 proxy cannot work in Chrome with any method.
  • Keep credentials out of source control. Read them from environment variables, for example os.environ["PROXY_USER"].

Because the handler lives in your script rather than in a browser profile, it also works in headless mode and on CI servers. Test it once with the IP check above: if the printed address is a residential IP in the country you chose, rather than your own, the proxy and the credentials are both working.

Key takeaways

  • Selenium sends traffic through the browser, so proxy settings go in the browser options.
  • --proxy-server sets host and port; it never carries a username or password.
  • For credentials, enable BiDi and call driver.network.add_auth_handler() before loading a page.
  • Chrome cannot authenticate to SOCKS5 proxies; use the HTTP endpoint with the same login.

The Chrome Extension Method After Chrome 137

Still useful, but not in regular Chrome

For years the standard answer to Selenium proxy authentication was to generate a tiny Chrome extension on the fly. The extension sets the proxy with the chrome.proxy API and answers the login challenge from a webRequest.onAuthRequired listener, and Selenium loads it when the browser starts. Most tutorials, and many packages that promise “authenticated proxies for Selenium”, still work this way.

That changed with Chrome 137. Google removed the --load-extension command-line switch from branded Chrome builds, which is exactly what ChromeDriver uses to load an unpacked extension. In regular Chrome the log now says the switch is not allowed and the extension is silently skipped, so the browser falls back to a sign-in prompt or a failed page. A temporary switch that restored the old behaviour has since been removed as well.

The switch still works in Chrome for Testing and in open-source Chromium, which is what Google recommends for automation anyway. If you already depend on an extension, for example to change headers as well as authenticate, point Selenium at one of those builds:

Python Β· extension in Chrome for Testing
options = webdriver.ChromeOptions()
options.binary_location = r"C:\tools\chrome-for-testing\chrome.exe"
options.add_argument(r"--load-extension=C:\tools\proxy-auth-ext")
driver = webdriver.Chrome(options=options)

The extension folder needs a Manifest V3 manifest.json with the proxy, webRequest and webRequestAuthProvider permissions, and a service worker that calls chrome.proxy.settings.set() and returns your credentials from onAuthRequired. Manifest V2 extensions, which most older snippets generate, are no longer supported in current Chrome. For new projects the BiDi handler is simpler: no files to generate, no special browser build, and nothing to rewrite when Chrome changes its extension rules again.

What Happened to selenium-wire

An archived project and its replacements

selenium-wire was the other classic answer. It ran a local man-in-the-middle proxy between the browser and your upstream proxy, so it could add credentials, rewrite headers and let you inspect every request from Python. The original article on this page recommended it too. Its author archived the repository on 3 January 2024, so it no longer receives fixes for new Chrome, Selenium or Python versions, and its bundled certificate handling makes it fragile on modern sites.

Almost everything people used it for now has a supported replacement in Selenium itself:

You used selenium-wire forUse now
Proxy username and passworddriver.network.add_auth_handler() with BiDi
Changing or blocking requestsBiDi request handlers in driver.network
Reading requests and responsesBiDi network events, or Chrome DevTools logging
A different upstream proxy per testA separate driver with its own --proxy-server

If you are starting a new scraper rather than maintaining a test suite, it is also worth looking at Playwright, which accepts a proxy server, username and password directly when you launch the browser or create a context. Our Python web scraping guide shows Playwright, requests and Scrapy with the same proxy.

Use a Proxy with Selenium and Firefox

Preferences for the host, BiDi for the login

Firefox reads its proxy settings from preferences rather than command-line flags, and GeckoDriver lets you set them on the options object. Proxy type 1 means manual configuration; set the same host and port for plain HTTP and for HTTPS.

Python Β· Firefox preferences
from selenium import webdriver

options = webdriver.FirefoxOptions()
options.enable_bidi = True
options.set_preference("network.proxy.type", 1)
options.set_preference("network.proxy.http", "v2.proxyempire.io")
options.set_preference("network.proxy.http_port", 5000)
options.set_preference("network.proxy.ssl", "v2.proxyempire.io")
options.set_preference("network.proxy.ssl_port", 5000)

driver = webdriver.Firefox(options=options)
handler_id = driver.network.add_auth_handler("YOUR_USERNAME", "YOUR_PASSWORD")
driver.get("https://api.ipify.org?format=json")

Firefox supports WebDriver BiDi natively, so the same authentication handler applies. As with Chrome, run the IP check once on your Firefox version before you scale up, and keep the handler registration before the first page load. The proxy capability from the Chrome section works in Firefox too, if you prefer one configuration for both browsers.

Rotating and Sticky IPs in Selenium

Choose per job, not per request

A browser is not a simple HTTP client. One page view triggers dozens of requests for scripts, styles, images and APIs, and Chrome keeps connections to the proxy open and reuses them. That shapes how rotation behaves in Selenium:

  • Sticky sessions keep the same exit IP for as long as you use the session ID, with no fixed time limit on ProxyEmpire. Use them for anything with continuity: logging in to your own test account, a multi-step form, a basket, or paging through results that must come from one visitor.
  • Rotating sessions give a fresh IP for new connections. In a browser, the reliable way to get a clean identity is a new driver with a new session, not a new page, because cookies, cache and open connections carry over inside one browser.
  • Location targeting goes in the username generated in the dashboard. Rotating residential and mobile proxies target country, region, city, ZIP, ISP and ASN at no extra charge, so one gateway serves every market you test.
Python Β· one browser per identity
import os
from selenium import webdriver

def new_driver(username):
    options = webdriver.ChromeOptions()
    options.enable_bidi = True
    options.add_argument("--proxy-server=http://v2.proxyempire.io:5000")
    options.add_argument("--headless")
    options.add_experimental_option(
        "prefs", {"profile.managed_default_content_settings.images": 2})
    driver = webdriver.Chrome(options=options)
    driver.network.add_auth_handler(username, os.environ["PROXY_PASS"])
    return driver

for user in [os.environ["PROXY_USER_US"], os.environ["PROXY_USER_DE"]]:
    d = new_driver(user)
    d.get("https://www.example.com/pricing")
    print(d.title)
    d.quit()

The prefs line turns off images, which cuts proxy traffic substantially on product and search pages while leaving the HTML and data you need. Close every driver with quit() in a finally block; abandoned headless browsers keep connections open and keep using traffic. ProxyEmpire has no limit on concurrent sessions, so you can run as many browsers in parallel as your machine or grid can handle. The sticky vs rotating guide goes deeper into the trade-offs, and mobile proxies are the right choice when you need to see a site the way 4G and 5G users see it.

Selenium Proxy Errors and How to Fix Them

What the error page is telling you
Error or symptomLikely causeFix
ERR_PROXY_CONNECTION_FAILEDWrong host or port, or a firewall blocking outbound traffic to the proxyCheck the gateway address and port; test with curl from the same machine
ERR_TUNNEL_CONNECTION_FAILEDThe proxy refused the tunnel: failed login, or a destination that is blockedCheck credentials; banking, payment and government sites are blocked by default
A sign-in prompt, or a 407 pageNo auth handler answered the challengeSet enable_bidi and register the handler before get()
ERR_NO_SUPPORTED_PROXIESA SOCKS5 proxy with credentials, or a malformed proxy stringUse http:// and the auth handler
“–load-extension is not allowed”Chrome 137 or newer ignores extension loadingSwitch to the BiDi handler or Chrome for Testing
Your own IP in the IP checkThe proxy option never reached the browserPass the options object to webdriver.Chrome(); check for typos in the flag
Same IP on every pageA sticky session, or connection reuse in one browserUse a new driver and session for a new identity

When the cause is not obvious, separate the proxy from Selenium. Run curl -x http://v2.proxyempire.io:5000 -U USERNAME:PASSWORD https://api.ipify.org from the same machine: if curl works and the browser does not, the problem is in your Selenium options; if curl fails too, it is the network, the credentials or the destination. ProxyEmpire’s support team is available 24/7 if you need a second pair of eyes on a connection.

Finally, a proxy changes where your traffic comes from, not how your automation behaves. Sites still notice hundreds of identical requests a second, so add realistic waits, reuse sessions where it makes sense and respect robots.txt and the site’s terms. Proxies are for lawful use; for background on collecting public data, see our web scraping basics.

ProxyEmpire proxies are for lawful use. Some site categories, such as banking, payment and government websites, are blocked by default on all products.

Selenium Proxy FAQ

Straight answers
How do I set a proxy in Selenium with Python?

Add --proxy-server=http://host:port to ChromeOptions, or set the proxy capability, and pass the options to webdriver.Chrome(). For proxies with a login, also enable BiDi and register driver.network.add_auth_handler().

Can I put the username and password in the proxy URL?

Not in Chrome. It ignores credentials in --proxy-server and shows a sign-in prompt instead. Use the BiDi authentication handler, or an extension in Chrome for Testing.

Why did my proxy authentication extension stop working?

Chrome 137 removed the --load-extension switch from branded Chrome builds, so ChromeDriver can no longer load unpacked extensions there. It still works in Chrome for Testing and Chromium.

Does Selenium support SOCKS5 proxies?

Yes, without a login: use --proxy-server=socks5://host:port. Chrome cannot authenticate to SOCKS5 proxies, so for authenticated sessions use the HTTP endpoint with the same credentials.

Is selenium-wire still maintained?

No. The repository was archived in January 2024. Selenium’s built-in BiDi network features now cover proxy authentication and request interception.

How do I use a different proxy for each browser?

Start a separate driver for each proxy or identity. Proxy settings apply to the whole browser, and one browser keeps cookies and connections between pages.

Which proxies work best with Selenium?

Rotating residential proxies suit most automation and testing across locations; mobile proxies suit mobile-first sites; datacenter proxies are the cheapest option for sites that do not filter by IP type.

References

Primary documentation
  1. Selenium: WebDriver BiDi network featuresAuthentication, request and response handlers
  2. Selenium: Browser optionsThe proxy capability and other shared options
  3. Selenium: Chrome specific functionalityArguments, extensions and browser location
  4. Chromium Extensions: removing –load-extension in Chrome buildsGoogle’s announcement for Chrome 137
  5. SeleniumHQ issue 15788Extension loading after the Chrome change
  6. selenium-wire on GitHubArchived on 3 January 2024

Proxies for your Selenium automation

One gateway for more than 30 million residential IPs and 4 million mobile IPs, sticky sessions with no fixed time limit, targeting by country, city, ZIP, ISP and ASN at no extra cost, no concurrent session limit and 24/7 support from real people. Try it for $1.97.

ProxyEmpire proxy types and prices

ProxyEmpire

Proxies for data collection at scale.

Residential, mobile, ISP and datacenter proxies for scraping, ad verification, SEO monitoring and multi-account work. Choose from 30M+ ethically sourced residential IPs and 4M+ 4G/5G mobile IPs in 170+ countries, target by country, city, ZIP, ISP or carrier, and connect over HTTP(S) or SOCKS5 with 99.9% uptime. Try residential and mobile proxies for $1.97.

🏘️ Rotating Residential Proxies
  • 30M+ Premium Residential IPs
  • Β 170+ Countries
    Every residential IP in our network corresponds to an actual desktop device with a precise geographical location. Our residential proxies are fast and reliable, with 99.9% uptime, and work for a wide range of use cases. You can use country, region, city, ZIP, ISP, ASN and OS fingerprint targeting for our rotating residential proxies, at no extra charge.

See our Rotating Residential Proxies

πŸ“ Static Residential Proxies
  • 24 Countries
    Buy a dedicated static residential IP from one of the 24 countries that we offer proxies in. Keep the same IP for a month or longer, while benefiting from their fast speed and stability.

See our Static Residential Proxies

πŸ“³ Rotating Mobile Proxies
  • 4M+ Premium Mobile IPs
  • Β 170+ Countries
    Access millions of clean mobile IPs with precise targeting by country, region, city, ZIP, mobile carrier, ASN and OS fingerprint, at no extra charge. Get far fewer IP blocks and CAPTCHAs with our 4G and 5G proxies.

See our Mobile Proxies

πŸ“± Dedicated Mobile Proxies
  • 4 Countries
  • 50+ Locations
    Get your own dedicated mobile proxy in one of our supported locations, with unlimited bandwidth and unlimited IP changes on demand. A great choice when you need a small number of mobile IPs and a lot of proxy bandwidth.

See our 4G & 5G Proxies

🌐 Rotating Datacenter Proxies
  • 197,000+ IPs Premium IPs
  • Β 62 Countries
    On a budget and need to do some simple scraping tasks? Our datacenter proxies are the perfect fit! Get started with as little as $2

See our Datacenter Proxies

♾️ Unlimited Residential Proxies
  • 1M+ Daily Residential IPs
  • 85+ Countries
    Our unlimited residential proxies bill by connection speed, not gigabytes: 100 to 1000 Mbit with unlimited bandwidth, on daily, weekly or monthly plans. Ask us for a 1-hour trial at 50 Mbit before you pick a plan.

See our Unlimited Residential Proxies

proxy locations

30M+ rotating IPs

99.9% uptime - high speed

99.9% uptime.

dedicated support team

24/7 Dedicated Support.

fair price

Fair Pricing.

🏠 Residential Proxies Rotating / Static / Unlimited
πŸ“± Mobile Proxies Rotating and Dedicated
πŸ–₯️ Datacenter Proxies Rotating
🌍 IP Pool 30M+ residential + 4M+ mobile IPs
πŸ“Ά Uptime 99.9% Β· Live status
πŸ’³ Payment Card Β· PayPal Β· Crypto Β· Bank transfer
πŸ’¬ Support 24/7 live chat Β· [email protected]