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.

5 min readTested on Selenium 4.51 · Chromium 155 · pproxy
Chrome under Selenium connects to a local forwarder without a login; the forwarder adds the proxy login and connects to the gateway.--proxy-server=http://USER:PASS@via.proxies.vision:9000This site can’t be reached · ERR_NO_SUPPORTED_PROXIESChrome127.0.0.1:8899Forwarderadds USER:PASSGateway:9000No login prompt for Chrome to answer.Selenium 4.51 · Chromium 155 · pproxy · October 14, 2026

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.

no login in --proxy-serverlocal forwarder

What we tried

Each attempt against our gateway, headless Chromium 155 under Selenium 4.51, loading ipinfo.io.

AttemptWhat 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 loginThe page loaded empty: a 407 with no prompt to answer, and no exception
--proxy-server=socks5://via.proxies.vision:11000net::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 → gatewayLoaded, German exit
Fig. 1 — Selenium 4.51.0 with Chromium 155 and chromedriver from Debian, October 14, 2026.

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:

bash
pip install pproxy
pproxy -l http://127.0.0.1:8899 -r "http://via.proxies.vision:9000#USER-country-de:PASS"
refused--proxy-server=http://USER:PASS@via.proxies.vision:9000login in the flag
200--proxy-server=http://127.0.0.1:8899forwarder adds the login

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.

selenium_proxy.py
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()
txt
94.134.94.118 DE AS8881 1&1 Versatel GmbH
94.134.94.118 DE AS8881 1&1 Versatel GmbH

Same 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?
No. Chrome refuses a ‑‑proxy-server value with credentials; in our test every page showed 'This site can’t be reached'. Pass only scheme, host and port, and supply the login another way.
Why does a proxied page load blank in headless Chrome?
The proxy asked for a login (407) and headless Chrome had no way to show the prompt. In our test the page simply came back empty, with no exception. Supply the login through a forwarder or an authentication handler.
Does Selenium's BiDi add_auth_handler work for proxies?
It is meant to: driver.network.add_auth_handler(user, password) with enable_bidi. With Selenium 4.51 and Chromium 155 from Debian it timed out in our runs and no page loaded. Try it in your setup; if it hangs too, use a local forwarder.
Is selenium-wire still a good option?
selenium-wire was archived by its author and is no longer maintained. A small local forwarder such as pproxy does the one thing you needed it for, adding the proxy login, with nothing patched into Selenium.
How do I get a new IP per page with Selenium?
Chrome keeps its connection to the proxy open, so pages can share an exit. Start a new driver (and forwarder) for a new IP, or use the sticky port with a session label when you want the same IP on purpose.

Residential proxies, pay per GB

HTTP and SOCKS5. Country, city and sticky sessions in the username. No subscription.