Selenium proxy with username and password in Chrome
Chrome won’t take a proxy login from the command line, and Selenium has no option that fixes that everywhere. We tried every route; one small local forwarder is what worked.
In short
Don’t put USER:PASS@ into --proxy-server; Chrome refuses it. Run a local forwarder without a login on 127.0.0.1 that adds your proxy login and connects to the gateway, and point Chrome at the forwarder.
What we tried
Each attempt against our gateway, headless Chromium 155 under Selenium 4.51, loading ipinfo.io.
| Attempt | What happened |
|---|---|
| --proxy-server=http://USER:PASS@via.proxies.vision:9000 | “This site can’t be reached”: Chrome refused the value |
| --proxy-server=http://via.proxies.vision:9000, no login | The page loaded empty: a 407 with no prompt to answer, and no exception |
| --proxy-server=socks5://via.proxies.vision:11000 | net::ERR_SOCKS_CONNECTION_FAILED |
| enable_bidi + driver.network.add_auth_handler(USER, PASS) | Timed out waiting for the BiDi command; no page loaded |
| --proxy-server=http://127.0.0.1:8899 → pproxy → gateway | Loaded, German exit |
Why Chrome makes this hard
Chrome takes its proxy from --proxy-server and has no flag for a proxy password. When the proxy asks for one, Chrome shows a login prompt, and a headless browser can’t. Puppeteer and Playwright answer that prompt for you; Puppeteer and Playwright have their own articles. Selenium’s classic WebDriver doesn’t.
SOCKS5 is no way out
Chrome has no SOCKS5 authentication at all, so a SOCKS5 proxy with a password fails with ERR_SOCKS_CONNECTION_FAILED. Use the HTTP port.
BiDi, in theory
Selenium 4 can answer the login through WebDriver BiDi: options.enable_bidi = True, then driver.network.add_auth_handler(user, password). In our runs it hung until Selenium gave up, every time, with and without a first navigation. It may work with another Chrome build or a later Selenium; check it in yours before you depend on it.
A forwarder is a tiny proxy on your own machine. Chrome connects to it without a login; it adds the login and passes the connection on to the gateway. Chrome never sees a 407, so there is nothing to answer. pproxy does it in one line:
pip install pproxy
pproxy -l http://127.0.0.1:8899 -r "http://via.proxies.vision:9000#USER-country-de:PASS"Keep it local
The forwarder accepts anyone who can reach its port, so bind it to 127.0.0.1 as above, never to 0.0.0.0 on a server. Targeting goes into the login after #, the same as anywhere else: -country-de, or a session label on the sticky port 10000 when one identity needs one IP.
A setup that works
The forwarder started from Python, Chrome pointed at it. In a container we also needed --no-sandbox and the Chromium path.
import json
import os
import subprocess
import time
from selenium import webdriver
USER = os.environ["PROXY_USER"]
PASS = os.environ["PROXY_PASS"]
def start_forwarder(targeting: str, port: int) -> subprocess.Popen:
"""A local proxy without a login that forwards to the gateway with one (pip install pproxy).
Chrome talks to 127.0.0.1 and never has to answer a login prompt."""
upstream = f"http://via.proxies.vision:9000#{USER}{targeting}:{PASS}"
proc = subprocess.Popen(["pproxy", "-l", f"http://127.0.0.1:{port}", "-r", upstream])
time.sleep(1) # let it bind the port
return proc
forwarder = start_forwarder("-country-de", 8899)
options = webdriver.ChromeOptions()
options.add_argument("--headless=new")
options.add_argument("--proxy-server=http://127.0.0.1:8899")
driver = webdriver.Chrome(options=options)
try:
for _ in range(2):
driver.get("https://ipinfo.io/json")
data = json.loads(driver.find_element("tag name", "body").text)
print(data["ip"], data["country"], data["org"])
finally:
driver.quit()
forwarder.terminate()94.134.94.118 DE AS8881 1&1 Versatel GmbH
94.134.94.118 DE AS8881 1&1 Versatel GmbHSame IP twice
Both loads left from one exit here: Chrome kept its connection open. In an earlier run the two loads got two IPs. Don’t rely on either. For a new IP, start a new driver; for a fixed one, use a session label on port 10000.
Can I put the proxy username and password into ‑‑proxy-server in Selenium?
Why does a proxied page load blank in headless Chrome?
Does Selenium's BiDi add_auth_handler work for proxies?
Is selenium-wire still a good option?
How do I get a new IP per page with Selenium?
Keep reading
aiohttp Proxy Errors: ClientHttpProxyError 407, SOCKS5 and trust_env
aiohttp's ClientHttpProxyError explained, why its message leaks your proxy password, socks5:// without aiohttp-socks, and rotation.
6 min read · #python#errors
Scrapy: Could not open CONNECT tunnel with proxy (407, 403, SOCKS5)
What the status in Scrapy's TunnelError means, why socks5:// only times out, and a proxy middleware that works. Every case reproduced.
7 min read · #scrapy#python
Tunnel connection failed: 407 in Python requests (and 403, 465)
The status is the proxy's answer, not the website's. Five causes of 407 we reproduced, and what 403 and 465 mean instead.
7 min read · #python#errors
Residential proxies, pay per GB
HTTP and SOCKS5. Country, city and sticky sessions in the username. No subscription.