Integration · Browser automation
How to use a proxy with Selenium (Python)
Selenium sets a proxy's host and port through options.proxy, but neither Selenium's proxy capability nor Chrome's --proxy-server has a field for a username and password. The dependable way to use an authenticated proxy is a small local forwarder: Selenium talks to it without credentials, and it adds them on the way to the ARC gateway.
Reviewed 30 Oct 2026
http://USERNAME _pool-arcfast _country-de:PASSWORD@ proxy.arcproxies.io:8000The short answer
- Selenium's
Proxyobject takes host and port only; it cannot carry proxy credentials. - A local
mitmdumpin upstream mode with--upstream-authadds the credentials, and--ignore-hosts '.*'keeps TLS untouched end to end. - One forwarder per identity: each sticky session or country gets its own local port.
- selenium-wire, the usual answer in older tutorials, has been archived and unmaintained since January 2024.
Setup
Set up Selenium with a proxy, step by step
- 01
Install Selenium and mitmproxy
Selenium 4 manages the driver itself through Selenium Manager. mitmproxy provides
mitmdump, the command-line forwarder.shell1 linepip install selenium mitmproxy - 02
Start a local forwarder with the credentials
Upstream mode sends every request to the ARC gateway with Basic proxy authentication added.
--ignore-hosts '.*'tells mitmproxy not to process any traffic, so HTTPS stays an untouched CONNECT tunnel and no certificate needs installing. The forwarder only listens on localhost.shell4 linesmitmdump --listen-host 127.0.0.1 -p 8081 \ --mode upstream:http://proxy.arcproxies.io:8000 \ --upstream-auth "USERNAME_pool-arcfast_country-de_session-k3f9a1x2_sesstime-10:PASSWORD" \ --ignore-hosts '.*' - 03
Point Chrome at the forwarder and check the exit IP
Set both
httpProxyandsslProxy: with onlyhttpProxy, HTTPS pages bypass the proxy. The IP echo page should show the gateway's exit, not your own address.check_exit.py17 linesfrom selenium import webdriverfrom selenium.webdriver.common.by import Byfrom selenium.webdriver.common.proxy import Proxy, ProxyTypeLOCAL = "127.0.0.1:8081" # the mitmdump forwarder, no credentials neededoptions = webdriver.ChromeOptions()options.add_argument("--headless=new")options.proxy = Proxy({"proxyType": ProxyType.MANUAL, "httpProxy": LOCAL, "sslProxy": LOCAL})driver = webdriver.Chrome(options=options)driver.set_page_load_timeout(30)try: driver.get("https://api.ipify.org?format=json") print(driver.find_element(By.TAG_NAME, "body").text) # {"ip":"…"}finally: driver.quit() - 04
One forwarder per session for parallel workers
A forwarder carries one username, so it carries one identity. For parallel workers, start one per worker on its own port, each with a fresh
_session-handle, and stop it when the worker finishes.workers.py28 linesimport secretsimport subprocessimport timefrom contextlib import contextmanagerBASE = "USERNAME_pool-arcfast_country-de" # pool + country; the session is added per worker@contextmanagerdef forwarder(port, username): proc = subprocess.Popen([ "mitmdump", "--listen-host", "127.0.0.1", "-p", str(port), "--mode", "upstream:http://proxy.arcproxies.io:8000", "--upstream-auth", f"{username}:PASSWORD", "--ignore-hosts", ".*", ], stdout=subprocess.DEVNULL) time.sleep(2) # give it a moment to bind try: yield f"127.0.0.1:{port}" finally: proc.terminate() proc.wait()for worker, port in enumerate(range(8081, 8085)): user = f"{BASE}_session-{secrets.token_hex(4)}_sesstime-15" with forwarder(port, user) as local: ... # build ChromeOptions with Proxy(httpProxy=local, sslProxy=local) as above - 05
Geo-target by changing the forwarder's username
Country and city live in the username, so a market is a forwarder.
_country-is a lowercase ISO code;_city-comes from the published list, lowercased without spaces.shell6 lines# Hamburg on port 8091, Paris on port 8092mitmdump --listen-host 127.0.0.1 -p 8091 --mode upstream:http://proxy.arcproxies.io:8000 \ --upstream-auth "USERNAME_pool-arcfast_country-de_city-hamburg:PASSWORD" --ignore-hosts '.*'mitmdump --listen-host 127.0.0.1 -p 8092 --mode upstream:http://proxy.arcproxies.io:8000 \ --upstream-auth "USERNAME_pool-arcfast_country-fr_city-paris:PASSWORD" --ignore-hosts '.*' - 06
Retry page loads that time out
Through a proxy, a page that never finishes loading is the usual failure. Set a page-load timeout, catch
TimeoutExceptionandWebDriverException, back off, and on the next attempt use a forwarder with a new handle. If the forwarder logs 407 responses, the credentials are wrong and retrying will not help.retry.py13 linesimport timefrom selenium.common.exceptions import TimeoutException, WebDriverExceptiondef load(driver, url, attempts=3): for attempt in range(attempts): try: driver.get(url) # raises TimeoutException after set_page_load_timeout return driver.page_source except (TimeoutException, WebDriverException) as err: print(f"attempt {attempt + 1}: {err.msg}") time.sleep(2 ** attempt) raise RuntimeError(f"gave up on {url}")
Why Selenium cannot pass proxy credentials directly
Selenium's proxy setting is the WebDriver proxy capability: a proxy type plus httpProxy, sslProxy and similar host:port fields. There is no username or password field, and Chrome's own --proxy-server switch has none either. When the gateway asks for credentials, a plain Selenium session has nothing to answer with.
| Approach | Status | Notes |
|---|---|---|
Local forwarder (mitmdump upstream mode) | Works; maintained | Browser-agnostic, no TLS interception with --ignore-hosts. One process per identity. |
WebDriver BiDi auth handler (driver.network.add_auth_handler) | In Selenium 4's Python API | The BiDi spec covers 407 challenges. It did not complete a proxy challenge in our test with Selenium 4.52 and Chrome, so test it on your versions before relying on it. |
| selenium-wire | Archived January 2024 | Read-only and unmaintained. Avoid for new code. |
Rotating vs sticky sessions with Selenium
A browser page load opens many connections, and a rotating username lets each one take a different exit. For Selenium, use a sticky session per worker: {{USER}}_session-<handle>_sesstime-<minutes>, with minutes that cover the whole task. For a new IP, start a new forwarder with a new handle rather than changing the old one.
Concurrency is then a question of how many forwarders and Chrome instances the machine can run, and how many requests per minute the target tolerates. Handling 429 rate limits covers backing off when it says no.
Firefox, Edge and other browsers
The forwarder is an ordinary unauthenticated HTTP proxy on localhost, so the same Proxy object works with webdriver.Firefox and webdriver.Edge. The proxy capability is part of the WebDriver standard, not a Chrome feature.
The problem
Common pitfalls
Setting only httpProxy
Without sslProxy, Chrome sends HTTPS pages directly, from your own IP. Set both to the forwarder's address.
Exposing the forwarder
Bind it to 127.0.0.1. A forwarder on 0.0.0.0 is an open proxy that spends your bandwidth for anyone who finds it.
Letting mitmproxy intercept TLS
Without --ignore-hosts '.*', mitmproxy decrypts HTTPS and Chrome rejects its certificate. Passing traffic through untouched is what you want here.
Browser background traffic
Chrome's own update and account checks also go through the proxy and count as bandwidth. They showed up in our forwarder logs within seconds of launch.
FAQ
Questions
Something not covered? Write to contact@arcproxies.io.
How do I use an authenticated proxy with Selenium in Python?
Run a local forwarder that adds the credentials, for example mitmdump --mode upstream:http://host:port --upstream-auth user:pass --ignore-hosts '.*', and point Selenium's Proxy object (httpProxy and sslProxy) at it. Selenium itself has no field for proxy credentials.
Is selenium-wire still maintained?
No. Its repository was archived on 3 January 2024 and the README says it is no longer maintained. It may still run, but it will not get fixes for new browser or Python versions.
Can I put the username and password in --proxy-server?
No. Chrome's --proxy-server takes a scheme, host and port. Credentials in that string are not used to answer the proxy's 407 challenge.
How do I rotate IPs in Selenium?
Start each worker with its own forwarder and a fresh _session- handle in the username, and quit both when the task ends. The next task gets a new handle and therefore a new exit IP.
Does this work with SOCKS5?
The forwarder talks to the gateway over HTTP, and the gateway also speaks SOCKS5 on the same port. For Selenium there is no advantage to SOCKS5: HTTPS still runs end to end inside a CONNECT tunnel.
Sources & review
Last reviewed by the ARC Proxies team. Prices are read from the live catalog at build time.
- [1]Selenium: Browser options: proxy
- [2]Selenium: WebDriver BiDi network features
- [3]mitmproxy: Options: upstream_auth, ignore_hosts, mode
- [4]GitHub, wkeeling/selenium-wire: Selenium Wire README (archived)
Next
What people build with it
Integrations
Other browser automation
Get started
Get credentials and paste them in.
Sign up, buy a gigabyte, generate a username in the dashboard. Code WELCOME15 takes 15% off your first purchase; bandwidth never expires.