๐ŸŽ‰ Premium Proxies ยท 3-Day Free TrialClaim Now โ†’
Guides

Selenium Proxy: Setup, Auth & Rotation (2026 Guide)

Set up a Selenium proxy in Python for Chrome and Firefox. Covers Selenium 4 Options, Selenium Wire auth, proxy rotation, and headless fixes with working code.

S SparkProxy 405 19 min read
Share
Selenium Proxy: Setup, Auth & Rotation (2026 Guide)

Setting up a Selenium proxy has one critical trap: Chrome silently ignores credentials in the --proxy-server flag. Passing --proxy-server=user:pass@host:port does nothing. Proxy authentication in Chrome needs IP whitelisting, Selenium Wire, or a packaged browser extension, and most guides skip that entirely. This guide covers every method for Selenium 4: ChromeOptions with the Service class, FirefoxOptions, the Proxy capability class, Selenium Wire for authenticated proxies, rotation patterns, headless considerations, and a complete error reference for Selenium datacenter proxy setups.

Why Use a Proxy with Selenium?

A browser automation proxy routes all browser traffic, including JavaScript-initiated requests, images, fonts, and API calls, through a datacenter IP. Unlike HTTP library proxies (requests, httpx), Selenium proxies affect the full browser network stack, making them essential for:

Use CaseWhy Proxy Is Required
Web scraping geo-restricted contentPresent a specific country's datacenter IP
Ad verificationVerify ad rendering from the target geography
Price monitoringPrevent single-IP rate limiting across hundreds of pages
Account managementAssign a stable IP per account to avoid association
QA on production sitesTest from specific regions without VPN

When you set a proxy on a Selenium driver, every request the browser makes (page loads, XHR/fetch calls, resource downloads) goes through the proxy. This is different from configuring a proxy in Python requests, which only affects your script's HTTP calls.

If your automation runs on Puppeteer rather than Selenium, the same proxy concepts apply with different syntax, covered in how to use proxies with Puppeteer.


Selenium Proxy via ChromeOptions (IP Whitelisting)

The simplest Chrome proxy setup uses the --proxy-server argument. This works for datacenter proxies that use IP whitelisting, where your machine's IP is authorized in the proxy dashboard, so no username or password is required.

from selenium import webdriver
from selenium.webdriver.chrome.options import Options

def create_driver_with_proxy(proxy_host: str, proxy_port: int) -> webdriver.Chrome:
    options = Options()
    options.add_argument(f"--proxy-server={proxy_host}:{proxy_port}")
    options.add_argument(f"--proxy-bypass-list=localhost,127.0.0.1")
    options.add_argument("--no-sandbox")
    options.add_argument("--disable-dev-shm-usage")
    return webdriver.Chrome(options=options)

driver = create_driver_with_proxy("your-proxy.sparkproxy.io", 10000)
driver.get("https://httpbin.org/ip")
print(driver.find_element("tag name", "pre").text)
driver.quit()

Selenium 4 note: Since Selenium 4.6, Selenium Manager downloads the matching chromedriver automatically, so webdriver.Chrome(options=options) runs with no driver path. If you need a specific binary, pass a Service object: from selenium.webdriver.chrome.service import Service, then driver = webdriver.Chrome(service=Service(executable_path="/path/to/chromedriver"), options=options). The proxy always goes on options, never on Service.

The --proxy-bypass-list argument accepts a comma-separated list of hostnames and IP ranges to bypass. Use to skip the proxy for all local addresses:

options.add_argument("--proxy-bypass-list=<local>,*.internal.company.com")

SOCKS5 proxy with ChromeOptions

options = Options()
options.add_argument("--proxy-server=socks5://your-proxy.sparkproxy.io:1080")
driver = webdriver.Chrome(options=options)

Note: Chrome's --proxy-server flag accepts http://, https://, and socks5:// schemes. For most datacenter proxy setups, omit the scheme entirely, and Chrome defaults to HTTP CONNECT tunneling when no scheme is specified.


Free trial

Scraping at scale? Skip the blocks.

Fast, unblockable datacentre proxies with unlimited bandwidth.

Selenium Proxy via the Proxy Capability Class

Selenium's official Proxy class provides a structured, browser-agnostic way to configure proxies, documented in the Selenium WebDriver Browser Options spec. It supports HTTP, HTTPS, SOCKS, FTP, and auto-configuration proxies.

from selenium import webdriver
from selenium.webdriver.common.proxy import Proxy, ProxyType
from selenium.webdriver.chrome.options import Options

# Build the proxy capability object
proxy = Proxy()
proxy.proxy_type = ProxyType.MANUAL
proxy.http_proxy  = "your-proxy.sparkproxy.io:10000"
proxy.ssl_proxy   = "your-proxy.sparkproxy.io:10000"  # HTTPS
proxy.no_proxy    = "localhost,127.0.0.1"

# Attach to ChromeOptions
options = Options()
options.proxy = proxy

driver = webdriver.Chrome(options=options)
driver.get("https://httpbin.org/ip")
driver.quit()

The ProxyType enum values relevant for datacenter proxies:

ProxyTypeDescription
`MANUAL`Explicit host:port for each protocol
`AUTODETECT`WPAD auto-detection
`PAC`Proxy Auto-Config file URL
`DIRECT`No proxy (bypass all)
`SYSTEM`Use OS proxy settings

For PAC file-based proxy configuration:

proxy = Proxy()
proxy.proxy_type = ProxyType.PAC
proxy.proxy_autoconfig_url = "http://proxy.company.com/proxy.pac"

options = Options()
options.proxy = proxy

Firefox Proxy with FirefoxOptions

Firefox proxy configuration uses preference keys rather than command-line arguments. These map directly to Firefox's about:config proxy settings.

from selenium import webdriver
from selenium.webdriver.firefox.options import Options as FirefoxOptions

def create_firefox_with_proxy(host: str, port: int) -> webdriver.Firefox:
    options = FirefoxOptions()

    # 0 = no proxy, 1 = manual, 2 = PAC, 4 = auto-detect, 5 = system
    options.set_preference("network.proxy.type", 1)

    # HTTP proxy
    options.set_preference("network.proxy.http", host)
    options.set_preference("network.proxy.http_port", port)

    # HTTPS proxy (uses same host/port for datacenter proxies)
    options.set_preference("network.proxy.ssl", host)
    options.set_preference("network.proxy.ssl_port", port)

    # SOCKS proxy (optional, set if using SOCKS5)
    # options.set_preference("network.proxy.socks", host)
    # options.set_preference("network.proxy.socks_port", socks_port)
    # options.set_preference("network.proxy.socks_version", 5)

    # Bypass list
    options.set_preference(
        "network.proxy.no_proxies_on",
        "localhost,127.0.0.1"
    )

    return webdriver.Firefox(options=options)

driver = create_firefox_with_proxy("your-proxy.sparkproxy.io", 10000)
driver.get("https://httpbin.org/ip")
driver.quit()

Firefox proxy with the Proxy class

You can also use the same Proxy capability object as Chrome:

from selenium.webdriver.common.proxy import Proxy, ProxyType
from selenium.webdriver.firefox.options import Options as FirefoxOptions

proxy = Proxy()
proxy.proxy_type = ProxyType.MANUAL
proxy.http_proxy = "your-proxy.sparkproxy.io:10000"
proxy.ssl_proxy  = "your-proxy.sparkproxy.io:10000"

options = FirefoxOptions()
options.proxy = proxy

driver = webdriver.Firefox(options=options)

Proxy Authentication in Chrome: The Core Problem

This is the most misunderstood part of Selenium proxy configuration:

Chrome does not support credentials in the --proxy-server flag.

# THIS DOES NOT WORK: credentials are silently ignored
options.add_argument("--proxy-server=user:pass@your-proxy.sparkproxy.io:10000")

Chrome was intentionally designed this way. The browser expects proxy credentials to come through authentication dialogs (which WebDriver can't reliably intercept) or through dedicated credential-storage APIs. There are three practical solutions:

Solution 2: Selenium Wire (see next section)

Selenium Wire intercepts the browser's proxy authentication challenge at the Python level and handles credential injection transparently. This is the recommended approach for credentials-based auth.

Solution 3: Chrome Extension for Proxy Auth

Create a packaged Chrome extension that handles the chrome.webRequest.onAuthRequired event. The extension provides credentials programmatically, bypassing the auth dialog.

import zipfile
import os
from selenium import webdriver
from selenium.webdriver.chrome.options import Options

def create_proxy_extension(host, port, username, password):
    """
    Creates a Chrome extension ZIP that handles proxy authentication.
    Returns the path to the generated .zip file.
    """
    manifest = """
    {
      "version": "1.0.0",
      "manifest_version": 2,
      "name": "Proxy Auth",
      "permissions": ["proxy", "tabs", "unlimitedStorage", "storage",
                      "<all_urls>", "webRequest", "webRequestBlocking"],
      "background": {"scripts": ["background.js"]},
      "minimum_chrome_version": "22.0.0"
    }
    """

    background_js = f"""
    var config = {{
        mode: "fixed_servers",
        rules: {{
            singleProxy: {{
                scheme: "http",
                host: "{host}",
                port: parseInt("{port}")
            }},
            bypassList: ["localhost"]
        }}
    }};

    chrome.proxy.settings.set({{value: config, scope: "regular"}}, function() {{}});

    function callbackFn(details) {{
        return {{
            authCredentials: {{
                username: "{username}",
                password: "{password}"
            }}
        }};
    }}

    chrome.webRequest.onAuthRequired.addListener(
        callbackFn,
        {{urls: ["<all_urls>"]}},
        ["blocking"]
    );
    """

    ext_path = "proxy_auth_extension.zip"
    with zipfile.ZipFile(ext_path, "w") as zf:
        zf.writestr("manifest.json", manifest)
        zf.writestr("background.js", background_js)

    return ext_path

# Use the extension
ext_path = create_proxy_extension(
    host="your-proxy.sparkproxy.io",
    port=10000,
    username="YOUR_USER",
    password="YOUR_PASS",
)

options = Options()
options.add_extension(ext_path)

driver = webdriver.Chrome(options=options)
driver.get("https://httpbin.org/ip")
driver.quit()

os.remove(ext_path)  # Clean up

Security note: The extension approach embeds credentials in the ZIP file in plain text. Use IP whitelisting or Selenium Wire in production, since both avoid storing credentials in files.


Proxy Rotation in Selenium

Each Selenium WebDriver session is bound to one proxy at launch, so the proxy cannot be changed mid-session via ChromeOptions. To rotate proxies, create a new driver instance for each proxy or IP.

For the full picture on rotation logic, proxy pools, and retry handling, see how to rotate proxies in Python.

Rotate by creating a new driver per session

from selenium import webdriver
from selenium.webdriver.chrome.options import Options
import random

PROXY_LIST = [
    "proxy1.sparkproxy.io:10000",
    "proxy2.sparkproxy.io:10001",
    "proxy3.sparkproxy.io:10002",
]

def get_driver(proxy: str) -> webdriver.Chrome:
    options = Options()
    options.add_argument(f"--proxy-server={proxy}")
    options.add_argument("--headless=new")
    options.add_argument("--no-sandbox")
    return webdriver.Chrome(options=options)

urls = [
    "https://httpbin.org/ip",
    "https://httpbin.org/user-agent",
    "https://httpbin.org/headers",
]

for url in urls:
    proxy = random.choice(PROXY_LIST)
    driver = get_driver(proxy)
    try:
        driver.get(url)
        print(f"[{proxy}] {driver.current_url}: OK")
    finally:
        driver.quit()  # Always quit to free resources

Rotate with Selenium Wire per request

Selenium Wire allows changing the proxy between requests within the same session using the driver.proxy property:

from seleniumwire import webdriver
import random

PROXIES = [
    {"http": "http://user:pass@proxy1.sparkproxy.io:10000",
     "https": "http://user:pass@proxy1.sparkproxy.io:10000"},
    {"http": "http://user:pass@proxy2.sparkproxy.io:10001",
     "https": "http://user:pass@proxy2.sparkproxy.io:10001"},
]

driver = webdriver.Chrome()

for url in ["https://httpbin.org/ip", "https://httpbin.org/ip"]:
    driver.proxy = random.choice(PROXIES)  # Rotate before each request
    driver.get(url)
    print(driver.find_element("tag name", "pre").text)

driver.quit()

driver.proxy assignment in Selenium Wire takes effect for all subsequent requests in the session, with no restart required.


Headless Chrome with a Proxy

Most Selenium proxy automation runs headless on a server. The proxy configuration is identical, but two things change: how you launch the browser and how easily anti-bot systems spot the session.

Use the modern headless mode and set a real window size so pages render the way they would on a desktop:

options = Options()
options.add_argument(f"--proxy-server={proxy_host}:{proxy_port}")
options.add_argument("--headless=new")           # modern headless (Chrome 109+)
options.add_argument("--window-size=1920,1080")  # avoid a tiny or zero viewport
options.add_argument("--disable-gpu")            # harmless on Linux servers
options.add_argument("--no-sandbox")
options.add_argument("--disable-dev-shm-usage")  # avoid /dev/shm crashes in Docker
driver = webdriver.Chrome(options=options)

Three things matter in production:

  • --headless=new vs legacy --headless. The new mode runs the same rendering path as headed Chrome, so sites see a more consistent fingerprint. The legacy flag is deprecated.
  • A proxy hides your IP, not your automation. Headless browsers still leak signals such as navigator.webdriver, a headless user-agent, and a missing window size. Pair a clean IP with --window-size, a real user-agent, and --disable-blink-features=AutomationControlled.
  • /dev/shm in Docker. Without --disable-dev-shm-usage, headless Chrome can crash on the small shared-memory segment inside containers.

A good proxy keeps your IP off the block list, but it will not rescue an obviously automated headless session. For the fingerprint and behavior side of the problem, see how to avoid getting your proxy blocked.


Selenium Grid and Remote WebDriver with Proxy

When running Selenium against a remote Grid node, pass the proxy via ChromeOptions before creating the remote session:

from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.proxy import Proxy, ProxyType

proxy = Proxy()
proxy.proxy_type = ProxyType.MANUAL
proxy.http_proxy = "your-proxy.sparkproxy.io:10000"
proxy.ssl_proxy  = "your-proxy.sparkproxy.io:10000"

options = Options()
options.proxy = proxy

# Remote Grid session (Selenium Grid 4 / cloud provider)
driver = webdriver.Remote(
    command_executor="http://selenium-hub:4444/wd/hub",
    options=options,
)

driver.get("https://httpbin.org/ip")
driver.quit()

Grid note: The proxy is configured in the browser running on the Grid node, not the machine running the test. The Grid node's IP must be whitelisted in your proxy dashboard (for IP auth), or credentials must be passed via Selenium Wire or the extension method.


Test Your Proxy in Selenium

Before running your actual automation, verify the proxy is routing correctly.

from selenium import webdriver
from selenium.webdriver.chrome.options import Options
import json

def verify_proxy(proxy_host: str, proxy_port: int) -> dict:
    options = Options()
    options.add_argument(f"--proxy-server={proxy_host}:{proxy_port}")
    options.add_argument("--headless=new")
    options.add_argument("--no-sandbox")
    options.add_argument("--disable-dev-shm-usage")

    driver = webdriver.Chrome(options=options)
    try:
        # Test 1: Check exit IP
        driver.get("https://httpbin.org/ip")
        ip_data = json.loads(driver.find_element("tag name", "pre").text)

        # Test 2: Check that HTTPS also routes through proxy
        driver.get("https://ipv4.icanhazip.com")
        https_ip = driver.find_element("tag name", "body").text.strip()

        return {
            "proxy": f"{proxy_host}:{proxy_port}",
            "exit_ip_httpbin": ip_data.get("origin"),
            "exit_ip_https":   https_ip,
            "status": "ok",
        }
    except Exception as e:
        return {"proxy": f"{proxy_host}:{proxy_port}", "status": "error", "error": str(e)}
    finally:
        driver.quit()

result = verify_proxy("your-proxy.sparkproxy.io", 10000)
print(result)
# {"proxy": "your-proxy.sparkproxy.io:10000", "exit_ip_httpbin": "1.2.3.4", ...}

Both exit_ip_httpbin and exit_ip_https should match the proxy's datacenter IP. If they are your real IP, the proxy is not being applied.

For a deeper checklist of proxy health checks beyond the exit IP, including DNS leaks and latency, see how to test if your proxy is working.


Common Selenium Proxy Errors and Fixes

Error / SymptomCauseFix
`--proxy-server=user:pass@host:port` ignoredChrome does not support credentials in this flagUse IP whitelisting, Selenium Wire, or the extension method
`ERR_TUNNEL_CONNECTION_FAILED`Proxy host unreachable, port blocked, or proxy downTest: `curl -x proxy-host:port https://httpbin.org/ip`; check port is open
`ERR_PROXY_AUTH_REQUIRED`Credentials required but not providedUse Selenium Wire or extension for auth; or add machine IP to whitelist
Browser opens, but real IP shown on target siteProxy set but not routing correctlyConfirm `--proxy-server` argument was added before `webdriver.Chrome()` call; check no typo in host
`WebDriverException: unknown error: net::ERR_NO_SUPPORTED_PROXIES`Wrong scheme used (e.g., `https://` proxy URL for Chrome)Remove the scheme from `--proxy-server` or use `http://`
Selenium Wire: `OSError: [Errno 48] Address already in use`Port conflict, Selenium Wire uses a local proxy listenerSet a custom port: `sw_options = {"port": 9999}`
Firefox: `SSL_ERROR_RX_RECORD_TOO_LONG`Proxy is not configured for HTTPS (only HTTP pref set)Set both `network.proxy.ssl` and `network.proxy.ssl_port` prefs
`SessionNotCreatedException: Chrome not reachable`Extension ZIP malformed or manifest errorValidate `manifest.json` has `"manifest_version": 2` and `background.scripts` field
Proxy works for first request, then 407 errorsIP whitelist entry expired (DHCP IP change)Use credential auth via Selenium Wire; or assign a static IP

Frequently asked questions

Yes. Each webdriver.Chrome() instance is independent, so pass a different proxy in each Options object. Parallel execution with tools like pytest-xdist or concurrent.futures.ThreadPoolExecutor creates separate driver instances, each with its own proxy configuration.

Yes. Add --headless=new to ChromeOptions alongside Selenium Wire. The seleniumwire_options proxy config applies whether the browser is headless or visible. Use options.add_argument("--headless=new") (the modern headless mode) rather than the legacy --headless flag.

A requests proxy only affects your Python script's HTTP calls. A Selenium proxy affects the entire browser: every resource the page loads (HTML, CSS, JavaScript, images, XHR, WebSocket) goes through the proxy. For web scraping where JavaScript makes API calls, a Selenium proxy is the correct choice. If you also run pure HTTP scrapers, see using proxies with Python requests and aiohttp.

Use options.add_argument("--proxy-server=socks5://your-proxy:1080") for IP-whitelisted SOCKS5. For authenticated SOCKS5, use Selenium Wire: sw_options = {"proxy": {"http": "socks5://user:pass@host:1080", "https": "socks5://user:pass@host:1080"}}.

Yes. The proxy configured at session launch applies to all requests in that driver session. The only way to change proxy mid-session natively is with Selenium Wire's driver.proxy property assignment or by quitting and creating a new driver.

Yes. With Selenium Wire, inspect driver.requests after any page load. Each request shows headers including any proxy-added Via or X-Forwarded-For headers. For plain Selenium, driver.get("https://ipv4.icanhazip.com") and driver.find_element("tag name", "body").text returns just the exit IP.

No. Since Selenium 4.6, Selenium Manager resolves and downloads the matching driver, so webdriver.Chrome(options=options) runs with no driver path. Pass a Service object only when you need a specific driver binary: from selenium.webdriver.chrome.service import Service, then driver = webdriver.Chrome(service=Service(executable_path="/path/to/chromedriver"), options=options). The proxy always belongs on options, never on Service.

selenium-wire imports an old blinker API. On blinker 1.8 and newer you get AttributeError: module 'blinker' has no attribute '_saferef' at import time, before any proxy code runs. Pin the compatible release with pip install "blinker==1.7.0", or switch to a maintained selenium-wire fork. It is a dependency conflict, not a proxy misconfiguration.

Yes, because the proxy only changes your IP. Headless Chrome still exposes automation signals such as navigator.webdriver, a headless user-agent, and a zero or tiny window size, and anti-bot systems weigh those on top of the IP reputation. Run --headless=new, set --window-size, supply a real user-agent, and add --disable-blink-features=AutomationControlled. A clean proxy plus a clean fingerprint is what gets you through.


Limited-time ยท 50% off

Get 50% off your first purchase

Premium datacentre proxies with unlimited bandwidth. Use the code at checkout.

Offer ends soon โ€” claim it before it's gone

Claim Discount

About the Author

SparkProxy Technical Team. The SparkProxy engineering team builds and maintains global datacenter and residential proxy infrastructure. This guide reflects proxy integration patterns tested with Selenium 4.20 and newer, recent Chrome and Firefox stable releases, and Selenium Wire 5.1.

Citations: Browser Options: proxy, Selenium WebDriver Documentation ยท selenium-wire, GitHub: wkeeling/selenium-wire

Keep reading

Related articles

How to Scrape Airbnb Listings and Prices

How to Scrape Airbnb Listings and Prices

Learn how to scrape Airbnb listings and prices: extract fields from Airbnb's embedded JSON, handle date-based pricing, map pagination, and anti-bot defenses.

SparkProxyยทGuides
How to Scrape GraphQL APIs

How to Scrape GraphQL APIs

Learn to scrape GraphQL API data: find the /graphql endpoint, read the query and variables in DevTools, then replay your own queries with cursor pagination.

SparkProxyยทGuides
How to Bypass reCAPTCHA When Web Scraping

How to Bypass reCAPTCHA When Web Scraping

How to bypass reCAPTCHA when web scraping the ethical way: how v2 and v3 scoring work, how to raise your reCAPTCHA score, and solvers as a last resort.

SparkProxyยทGuides