We use cookies to enhance user experience, personalize content, and analyze traffic. Cookie Policy

← Back to all articles

Selenium Proxy in Python with Chrome: Setup and Auth

Set a Selenium proxy in Python with Chrome via --proxy-server in ChromeOptions. Learn why Chrome ignores user:pass and which auth methods work.

by Unknown Proxies

13 min read

June 18, 2026

Updated October 17, 2026

Selenium Proxy in Python with Chrome: Setup and Auth

To use a proxy with Selenium in Python and Chrome, add --proxy-server=http://host:port to ChromeOptions before you create the driver. That part is easy. Authentication is where most setups break: Chrome ignores a username and password inside --proxy-server, so http://user:pass@host:port does not log in to the proxy.

Quick answer:

Python Selenium proxy request flow from WebDriver to browser, proxy, and target site

Set a Selenium proxy in Python with Chrome

Pass the proxy as a Chrome command-line argument through ChromeOptions:

from selenium import webdriver

options = webdriver.ChromeOptions()
options.add_argument("--proxy-server=http://proxy.example.com:8080")

driver = webdriver.Chrome(options=options)
try:
    driver.get("https://ipv4.unknownproxies.com/ip")
    print(driver.find_element("tag name", "body").text)
finally:
    driver.quit()

The page should print the proxy's exit IP, not your own. If it prints your own IP, Chrome did not receive the argument. Check that you passed the same options object to webdriver.Chrome().

Timing matters. Chrome reads --proxy-server once, at launch. If you change the proxy string after webdriver.Chrome() returns, the running browser keeps the old route. Set a custom Selenium user agent the same way, before launch, so the browser profile and the route stay the same for the whole session.

Chromium also accepts a bypass list for hosts that must go direct:

options.add_argument("--proxy-server=http://proxy.example.com:8080")
options.add_argument("--proxy-bypass-list=localhost;127.0.0.1")

For a SOCKS5 proxy, use --proxy-server=socks5://proxy.example.com:1080. Chrome supports no authentication methods for SOCKS5, so a SOCKS5 proxy must be allowlisted by IP. Our SOCKS5 vs HTTP proxy comparison explains when each protocol fits.

Why Chrome ignores user:pass in --proxy-server

Many tutorials show this:

# Does not authenticate in Chrome
options.add_argument("--proxy-server=http://USER:PASS@proxy.example.com:8080")

Chromium's proxy support documentation is direct about it: Chrome does not implement credentials embedded in proxy settings and will not use them. The proxy answers with 407 Proxy Authentication Required, and Chrome shows a sign-in dialog or a blank page. WebDriver cannot type into that dialog.

So the fix is not a different URL format. You need one of these:

Method Works with Code needed Notes
IP allowlisting HTTP and SOCKS5 None Simplest. Your machine needs a stable public IP
Selenium BiDi auth handler HTTP proxies A few lines Needs a recent Selenium 4 release and enable_bidi
Manifest V3 extension HTTP proxies Two small files Branded Chrome 137+ ignores --load-extension
Local forwarding proxy HTTP and SOCKS5 Separate process The forwarder adds credentials upstream
Selenium Wire Not recommended Archived, no longer maintained

Authenticated proxy options for Selenium Chrome

Before you change browser code, confirm the credentials work outside Selenium:

curl -x "http://USERNAME:PASSWORD@proxy.example.com:8080" https://ipv4.unknownproxies.com/ip

If curl fails, the problem is the host, port, credentials, or plan, not Selenium. Our guide to fixing 407 Proxy Authentication Required covers that layer. If curl works and Chrome does not, continue below.

Option 1: IP allowlisting

If your proxy plan lets you allowlist IP addresses, add the public IP of the machine that runs Chrome. Then use the plain --proxy-server=http://host:port setup above with no credentials at all. This works in headless mode, with SOCKS5, and in every Chrome version.

It is a poor fit when your scripts run on machines with changing IPs, such as some CI runners or laptops on different networks.

Option 2: Selenium BiDi auth handler

Selenium 4 has a WebDriver BiDi network module. In recent Python releases, driver.network.add_auth_handler(username, password) answers HTTP authentication challenges for you. The method and its remove_auth_handler(callback_id) counterpart are listed in the Selenium Python API reference, and the BiDi network documentation says BiDi must be enabled in the browser options. As of October 2026 the latest release on PyPI is Selenium 4.51.0, which needs Python 3.10 or newer.

import os

from selenium import webdriver

options = webdriver.ChromeOptions()
options.enable_bidi = True
options.add_argument("--proxy-server=http://proxy.example.com:8080")

driver = webdriver.Chrome(options=options)
handler_id = driver.network.add_auth_handler(
    os.environ["PROXY_USER"],
    os.environ["PROXY_PASS"],
)

try:
    driver.get("https://ipv4.unknownproxies.com/ip")
    print(driver.find_element("tag name", "body").text)
finally:
    driver.network.remove_auth_handler(handler_id)
    driver.quit()

Two limits. First, the Python BiDi API changed across Selenium 4 releases, so older versions may not have add_auth_handler. Upgrade with pip install -U selenium and check the method exists. Second, Selenium's documentation shows the handler answering a website's HTTP auth prompt, not a proxy 407. Confirm on your Chrome version that it also answers your proxy's challenge before you rely on it. The exit-IP check above shows the result in one request.

Option 3: Manifest V3 extension

A small extension can answer the proxy challenge with chrome.webRequest.onAuthRequired. Current Chrome needs Manifest V3: Chrome 139 removed Manifest V2 support completely, as the Manifest V2 deprecation timeline states. Many older Selenium proxy-auth snippets use "manifest_version": 2 and no longer load.

manifest.json:

{
  "manifest_version": 3,
  "name": "Proxy auth",
  "version": "1.0",
  "permissions": ["webRequest", "webRequestAuthProvider"],
  "host_permissions": ["<all_urls>"],
  "background": { "service_worker": "background.js" }
}

background.js:

const USERNAME = "PROXY_USER";
const PASSWORD = "PROXY_PASS";

chrome.webRequest.onAuthRequired.addListener(
  (details, callback) => {
    if (details.isProxy) {
      callback({ authCredentials: { username: USERNAME, password: PASSWORD } });
    } else {
      callback({});
    }
  },
  { urls: ["<all_urls>"] },
  ["asyncBlocking"]
);

The webRequest API reference documents the webRequestAuthProvider permission, asyncBlocking, and the isProxy field. The isProxy check makes sure the extension only answers the proxy, not website logins.

Load the folder and keep --proxy-server for routing:

from selenium import webdriver

options = webdriver.ChromeOptions()
options.binary_location = "/path/to/chrome-for-testing/chrome"
options.add_argument("--proxy-server=http://proxy.example.com:8080")
options.add_argument("--load-extension=/abs/path/to/proxy-auth-extension")

driver = webdriver.Chrome(options=options)

The catch: starting in Chrome 137, branded Google Chrome ignores --load-extension. This also breaks Selenium's extension loading in branded Chrome. Chrome for Testing and Chromium still accept the flag, which is why the example sets binary_location. Selenium Manager can download Chrome for Testing for you when you request a browser version that is not installed.

Generate the extension folder from environment variables at runtime, and delete it afterwards. Do not commit credentials inside background.js.

Option 4: Local forwarding proxy

Run a small proxy on 127.0.0.1 that accepts connections from Chrome without auth and forwards them to the real proxy with credentials. Chrome then uses --proxy-server=http://127.0.0.1:PORT. This works in every browser and Chrome version, but it is one more process to run and monitor. Bind it to localhost only, or anyone on your network can use your proxy account.

Selenium Wire is archived

Selenium Wire used to be the standard answer for authenticated proxies. Its GitHub repository was archived on January 3, 2024, with the notice "Selenium Wire is no longer being maintained", and the last PyPI release is 5.1.0 from October 2022. Existing scripts may still run, but new Selenium and Chrome releases receive no fixes. Prefer one of the options above for new code.

ChromeOptions vs the Selenium Proxy object

Selenium also has a browser-neutral Proxy class. In current Selenium 4, assign it to options.proxy, as the Selenium options documentation shows:

from selenium import webdriver
from selenium.webdriver.common.proxy import Proxy, ProxyType

options = webdriver.ChromeOptions()
options.proxy = Proxy({
    "proxyType": ProxyType.MANUAL,
    "httpProxy": "proxy.example.com:8080",
    "sslProxy": "proxy.example.com:8080",
})

driver = webdriver.Chrome(options=options)

The Proxy constructor takes a dict. These keys come from the Selenium Python Proxy reference:

Key What it sets
proxyType ProxyType.MANUAL, PAC, AUTODETECT, SYSTEM, or DIRECT
httpProxy, sslProxy host:port for HTTP and HTTPS traffic
socksProxy, socksVersion host:port and an int such as 5 for a SOCKS proxy
noProxy Hosts that skip the proxy, as a string or a list of strings
proxyAutoconfigUrl PAC file URL, used with ProxyType.PAC

Older tutorials pass desired_capabilities to webdriver.Chrome(). Current Selenium 4 releases do not accept that argument, so that code fails with a TypeError. The Proxy object helps when you share one configuration between Chrome and Firefox. For Chrome only, --proxy-server is shorter and easier to debug. Neither one adds proxy authentication.

Selenium proxy in Python with Firefox

Firefox uses the same Proxy object. Set it on FirefoxOptions before you create the driver:

from selenium import webdriver
from selenium.webdriver.common.proxy import Proxy, ProxyType

options = webdriver.FirefoxOptions()
options.proxy = Proxy({
    "proxyType": ProxyType.MANUAL,
    "httpProxy": "proxy.example.com:8080",
    "sslProxy": "proxy.example.com:8080",
    "noProxy": ["localhost", "127.0.0.1"],
})

driver = webdriver.Firefox(options=options)
try:
    driver.get("https://ipv4.unknownproxies.com/ip")
    print(driver.find_element("tag name", "body").text)
finally:
    driver.quit()

Authentication has the same shape as in Chrome. Credentials in the proxy address do not log in, and Selenium maintainers said in a Selenium issue about Python proxy auth that Firefox does not support proxy authentication through the proxy settings either. Use IP allowlisting, a local forwarding proxy, or the BiDi auth handler from Option 2 with options.enable_bidi = True. The Manifest V3 extension route is Chrome-specific.

Proxy formats Selenium and Chrome accept

Proxy format Example Where it works
Host and port proxy.example.com:8080 Proxy object fields, --proxy-server (HTTP assumed)
HTTP URL http://proxy.example.com:8080 --proxy-server
SOCKS5 URL socks5://proxy.example.com:1080 --proxy-server, IP-allowlisted proxies only
Auth URL http://user:pass@proxy.example.com:8080 curl and Python requests, not Chrome

Proxy dashboards often give lines like host:port:username:password. Split the host and port for --proxy-server and keep the credentials for your auth handler or extension. The proxy converter rewrites whole lists between these formats.

Rotating proxies with Selenium

Selenium does not rotate a browser's proxy per request. Treat each proxy as part of one browser session and start a new driver to change it:

from contextlib import suppress
from selenium import webdriver

proxies = [
    "http://proxy-1.example.com:8080",
    "http://proxy-2.example.com:8080",
    "http://proxy-3.example.com:8080",
]

for proxy in proxies:
    options = webdriver.ChromeOptions()
    options.add_argument(f"--proxy-server={proxy}")

    driver = webdriver.Chrome(options=options)
    try:
        driver.get("https://example.com")
        # Run one bounded workflow for this proxy.
    finally:
        with suppress(Exception):
            driver.quit()

This is slower than changing a proxy in an HTTP client such as Python Requests, but it is more reliable. A browser session holds cookies, local storage, service workers, and open connections. If you change the network route in the middle of a flow, the site sees one session jump between IPs.

You can also keep one endpoint and let the provider rotate behind it. A rotating gateway gives a new IP per connection, and a sticky session keeps one IP for a set time. Our guide to sticky vs rotating proxies explains which session type fits logins, carts, and multi-step forms. If you need several proxies inside one browser process, Playwright supports a proxy per browser context; see the Playwright proxy reference. Puppeteer does the same with one browser context per proxy.

Residential vs ISP proxies for Selenium

Workflow Better fit Why
Localized public page checks Residential Broad geography and consumer IPs
Repeated account workflow ISP Stable dedicated IPs and consistent sessions
Many independent pages Residential Rotation spreads load where the site allows it
QA from one market ISP or sticky residential Consistency matters more than rotation
Retail and ticketing checkout tests ISP or sticky residential One IP has to last the whole checkout
Retail monitoring Depends Stability, latency, and site tolerance all matter

Unknown Proxies sells both residential proxies and ISP proxies. If the site expects the same visitor across several browser actions, a stable session matters more than a large list. If each page view is independent, rotation reduces how often one IP repeats.

Debugging a Selenium proxy

Change one thing at a time:

  1. Test the proxy outside Selenium with curl.
  2. Launch Chrome with one proxy and open an IP echo page.
  3. Confirm HTTPS pages load, not only plain HTTP.
  4. Check whether the proxy returns 407.
  5. Run a visible browser with no extensions or custom profile while you debug.
  6. Add a bypass list only for hosts that must go direct.
  7. Compare the same target with and without the proxy.
Symptom Likely cause First fix
Pages never load Wrong host, port, or protocol, or the proxy is down Test with curl
Sign-in dialog or blank page Chrome ignored user:pass in --proxy-server Use allowlisting, a BiDi auth handler, or an MV3 extension
Extension does nothing Branded Chrome 137+ ignored --load-extension, or the manifest is V2 Use Chrome for Testing and Manifest V3
TypeError on desired_capabilities Old tutorial code Use options.proxy or --proxy-server
Only localhost breaks Chrome sends local traffic through the proxy Add --proxy-bypass-list
Target returns 403 or 429 Site rules, rate limit, or session mismatch Slow down and debug behavior, not the proxy setting

A 403 or 429 from the target means the request went through the proxy and the site chose not to serve it. The proxy setting is working at that point.

Selenium proxy debugging checklist for curl, browser launch, authentication, HTTPS, and target response checks

Headless mode and proxy testing

Headless mode hides the proxy sign-in dialog, so a failed login looks like an empty page. Get the proxy working in a visible browser first:

options = webdriver.ChromeOptions()
options.add_argument("--proxy-server=http://proxy.example.com:8080")
# Do not add --headless until the proxy works.
driver = webdriver.Chrome(options=options)

Then enable headless mode and test again:

options.add_argument("--headless=new")

If the visible browser works and headless fails, compare flags, extensions, and how the auth step runs. Keep the same proxy during the test so you do not confuse proxy quality with browser mode.

Session and rate-limit guidelines

One driver.get() loads HTML, scripts, images, fonts, and XHR calls, all through the same proxy. Browser automation sends many more requests than one HTTP call.

If the site starts returning 429, back off and honor Retry-After; our HTTP 429 Too Many Requests guide covers backoff in detail.

FAQ

How do I use a proxy with Selenium in Python and Chrome?

Create webdriver.ChromeOptions(), call options.add_argument("--proxy-server=http://host:port"), and pass the options to webdriver.Chrome(options=options). Open an IP echo page to confirm the exit IP.

Why does Chrome ignore the username and password in my proxy URL?

Chrome does not use credentials embedded in proxy settings. Chromium's documentation says so directly. Use IP allowlisting, a Selenium BiDi auth handler, a Manifest V3 extension, or a local forwarding proxy.

How do I set a proxy in Selenium with Firefox in Python?

Create webdriver.FirefoxOptions(), assign options.proxy = Proxy({"proxyType": ProxyType.MANUAL, "httpProxy": "host:port", "sslProxy": "host:port"}), and pass the options to webdriver.Firefox(options=options). Like Chrome, it does not log in with credentials in the address.

Is Selenium Wire still maintained?

No. The GitHub repository was archived on January 3, 2024, and the last release, 5.1.0, is from October 2022. Old scripts may still run, but use another auth method for new projects.

Can I use a SOCKS5 proxy with authentication in Selenium Chrome?

Not directly. Chrome supports no SOCKS5 authentication methods. Use IP allowlisting, an HTTP endpoint from the same provider, or a local forwarder that handles the upstream login.

Can I change the proxy without restarting Selenium?

Not with --proxy-server. Chrome reads it at launch. Start a new driver per proxy, or use a rotating gateway endpoint that changes the IP on the provider side.

Why does my proxy work with curl but not Chrome?

curl accepts user:pass@host:port, and Chrome does not. If the credentials work in curl, the proxy is fine. Fix the auth step in the browser instead.

Final thoughts

Set --proxy-server before launch, verify the exit IP, and solve authentication on purpose instead of with a URL Chrome ignores. IP allowlisting is the least fragile choice. BiDi auth handlers and Manifest V3 extensions work when you need username and password auth. For stable dedicated sessions in browser automation, see ISP proxy pricing.

About the Author

Unknown Proxies

Proxy Infrastructure Team

Stay Unknown

High-performance dedicated proxies optimized for speed and reliability. Get uncompromising quality, 99.9% uptime, and unmatched support. Stay Unknown.

Explore Plans