Selenium Proxy in Python: Chrome, Edge, Auth and SOCKS5

Published:

14 minute read

Updated:

PXNET
Written by: PXNET
Very faint IP rows, a plus-cornered frame with the Proxynet wordmark, an x and the Selenium logo, tagged INTEGRATION

Your Selenium script works, but you need the pages as a visitor in Germany sees them, or the job must run from a server the site already rate-limits. You add a proxy and get a blank page with no error. The cause is usually the password: the WebDriver standard has no field for proxy credentials, so user:pass@host:port is either rejected or silently ignored.

This guide covers the proxy setup for Chrome, Edge and Firefox, the authentication methods that work today, SOCKS5, HTTPS, rotation and the error messages. Every Python example was run with Selenium 4.49, Chrome 154, Edge 153 and Firefox 156 on Windows 11 against a local test proxy that requires a username and password.

What is Selenium?

Selenium drives a real web browser from code, mostly to test web applications and also to collect data from pages that only render with JavaScript. It opens pages, clicks, types, submits forms and reads the page content the way a user would. Your code calls the Selenium library, the library talks to a driver (ChromeDriver, msedgedriver or geckodriver), and the driver controls the browser through the W3C WebDriver protocol.

Selenium supports Chrome, Edge, Firefox and Safari, with official bindings for Python, Java, C#, Ruby and JavaScript. Since version 4.6 it ships with Selenium Manager, which finds the installed browser and downloads the matching driver. Still choosing a tool? See Playwright vs Selenium.

How does a proxy work in Selenium?

In Selenium the proxy belongs to the browser session, not to a single request:

  1. You put the proxy into the browser options, as the Chrome argument --proxy-server or as a Proxy object (the proxy capability of the WebDriver standard).
  2. The driver starts the browser with that setting, and it applies to every tab until driver.quit().
  3. For an https:// page the browser sends CONNECT host:443 to the proxy and opens an encrypted tunnel. For an http:// page it sends the full URL to the proxy.
  4. If the proxy needs a login, it answers 407 Proxy Authentication Required. This is the step the WebDriver standard does not cover.
  5. The target site sees the proxy's exit IP. For a different proxy, you start a new browser.

Chrome also sends its own background traffic (updates, account services) through the proxy, which counts on a plan billed per gigabyte.

How do you use Selenium with a proxy in Python?

Step 1: Install Selenium. Selenium 4.49 needs Python 3.10 or later:

bash
pip install selenium

Step 2: Get a proxy server. Choose a product on the proxy page; the Endpoint Generator in the Proxynet dashboard gives you the gateway address, the port and a username that carries the country, city and session you picked. HTTP and SOCKS5 use separate ports on the same gateway.

Step 3: The WebDriver. The earlier version of this guide said to download ChromeDriver by hand and pass executable_path. That parameter is gone: Selenium Manager downloads the driver on the first run. If the machine needs a proxy to reach the internet, set SE_PROXY for Selenium Manager's downloads.

Step 4: Start the browser with the proxy. For a proxy that does not ask for a password (an IP whitelist, covered below):

python
from selenium import webdriver
from selenium.webdriver.common.by import By

PROXY = "http://pr.proxynet.io:8000"

options = webdriver.ChromeOptions()
options.add_argument(f"--proxy-server={PROXY}")
options.add_argument("--headless")  # remove to watch the browser

driver = webdriver.Chrome(options=options)
try:
    driver.get("https://api.ipify.org/?format=json")
    print(driver.find_element(By.TAG_NAME, "body").text)  # {"ip": "<exit IP>"}
finally:
    driver.quit()

The printed IP should be the proxy's exit IP. Selenium's Proxy object does the same job (options.proxy = Proxy({"proxyType": ProxyType.MANUAL, "httpProxy": "host:port", "sslProxy": "host:port"})), and in our test both forms routed the traffic the same way.

How do you use a proxy with a username and password in Selenium?

The W3C WebDriver proxy definition has keys for proxy addresses but none for a username or password, and Chromium's proxy documentation says Chrome does not use credentials embedded in the proxy settings. There is no --proxy-auth argument either; the old code in this post used one. Our tests against a proxy that requires a login:

What we triedWhat happened
--proxy-server=http://host:port, no credentialsThe proxy returned 407; no exception, blank page
--proxy-server=http://user:pass@host:portERR_NO_SUPPORTED_PROXIES error page, no exception
user:pass@host:port in the Proxy objectThe page loaded, but Chrome connected directly, from our own IP
Selenium BiDi driver.network.add_auth_handler(), ChromeAnswering the challenge failed with Invalid InterceptionId, then a timeout
CDP Fetch.continueWithAuth (helper below)Worked in Chrome 154 and Edge 153
Port without a login (IP whitelist)Worked

The third row is the dangerous one: the script seems to work while the traffic skips the proxy.

Route 1: IP whitelist

Add the public IP of the machine that runs the script to the proxy's allowed list, and the proxy stops asking for a password. The Step 4 code then works unchanged, and so do the Edge and Firefox versions. Proxynet accepts up to 10 IPs. This suits a server with a fixed IP, not a laptop whose IP changes; see Proxy Authentication: User:Pass vs IP Whitelist.

Route 2: answer the 407 over the Chrome DevTools Protocol

Chrome and Edge expose the Fetch domain of the DevTools Protocol. With handleAuthRequests on, the browser pauses a request that needs credentials and fires Fetch.authRequired, whose source is Server or Proxy. The helper answers only proxy challenges; websocket-client comes with Selenium.

python
# proxy_auth.py
import json
import threading
import urllib.request

import websocket  # installed together with selenium (websocket-client)


def attach_proxy_auth(driver, username, password):
    """Answers the tab's proxy 407 challenges over the Chrome DevTools Protocol."""
    caps = driver.capabilities
    vendor = caps.get("goog:chromeOptions") or caps.get("ms:edgeOptions")  # Chrome or Edge
    address = vendor["debuggerAddress"]
    with urllib.request.urlopen(f"http://{address}/json") as resp:
        targets = json.load(resp)
    page = next(t for t in targets if t["type"] == "page")
    ws = websocket.create_connection(page["webSocketDebuggerUrl"], suppress_origin=True)

    counter = {"id": 0}

    def send(method, params=None):
        counter["id"] += 1
        ws.send(json.dumps({"id": counter["id"], "method": method, "params": params or {}}))

    def listen():
        while True:
            try:
                msg = json.loads(ws.recv())
            except Exception:
                return  # the browser was closed
            method = msg.get("method")
            params = msg.get("params", {})
            if method == "Fetch.requestPaused":
                send("Fetch.continueRequest", {"requestId": params["requestId"]})
            elif method == "Fetch.authRequired":
                if params["authChallenge"]["source"] == "Proxy":
                    answer = {"response": "ProvideCredentials",
                              "username": username, "password": password}
                else:
                    answer = {"response": "Default"}  # the site's own login is not ours to answer
                send("Fetch.continueWithAuth", {"requestId": params["requestId"],
                                                "authChallengeResponse": answer})

    send("Fetch.enable", {"handleAuthRequests": True})
    while json.loads(ws.recv()).get("id") != counter["id"]:
        pass  # wait until interception is active before the first driver.get()
    threading.Thread(target=listen, daemon=True).start()
    return ws

Using it takes one line after the driver starts:

python
from selenium import webdriver
from proxy_auth import attach_proxy_auth

options = webdriver.ChromeOptions()
options.add_argument("--proxy-server=http://pr.proxynet.io:8000")
driver = webdriver.Chrome(options=options)
attach_proxy_auth(driver, "user", "pass")
driver.get("https://api.ipify.org/?format=json")

Limits: the helper covers only the tab it attached to, so a new window starts without it. With a wrong password Chrome shows ERR_TOO_MANY_RETRIES.

What about Selenium Wire and proxy extensions?

The Selenium Wire repository was archived on 3 January 2024, and in a fresh Python 3.13 environment from seleniumwire import webdriver failed for us with ModuleNotFoundError: No module named 'pkg_resources'. Auth extensions depended on the --load-extension switch, which official Chrome builds ignore from version 137; only Chrome for Testing and Chromium still accept it. SeleniumBase accepts proxy="user:pass@host:port" and handles the login itself; see How to Use Proxy with SeleniumBase?.

Which proxy method should you use in Selenium?

MethodChrome / EdgeFirefoxPasswordStatus in 2026
--proxy-server argumentYesNo, use preferencesNoCurrent
Proxy objectYesYesIgnoredCurrent
IP whitelistYesYesNot neededCurrent
CDP helperYesNoYesTested, works
BiDi add_auth_handlerFailed in ChromeLogin prompt errorIn theoryNot reliable yet
Selenium WireImport failsImport failsYesArchived
Auth extensionChrome for Testing onlySeparate formatYesBroken in Chrome 137+

How do you set a proxy for Microsoft Edge in Selenium?

Edge is built on Chromium, so EdgeOptions takes the same argument, and Selenium Manager downloads msedgedriver:

python
from selenium import webdriver

options = webdriver.EdgeOptions()
options.add_argument("--proxy-server=http://pr.proxynet.io:8000")
driver = webdriver.Edge(options=options)

The CDP helper works with Edge too; with Edge 153 and the password-protected test proxy, the page loaded through the proxy.

How do you set a proxy in Selenium with Firefox?

Firefox does not read --proxy-server. Set its network preferences, or pass the Proxy object, which geckodriver turns into the same preferences. Firefox was not installed on our test machine; Selenium Manager downloaded Firefox 156 on the first run.

python
from selenium import webdriver

options = webdriver.FirefoxOptions()
options.set_preference("network.proxy.type", 1)  # 1 = manual configuration
options.set_preference("network.proxy.http", "pr.proxynet.io")
options.set_preference("network.proxy.http_port", 8000)
options.set_preference("network.proxy.ssl", "pr.proxynet.io")  # used for https:// pages
options.set_preference("network.proxy.ssl_port", 8000)
driver = webdriver.Firefox(options=options)

For SOCKS5, set network.proxy.socks, network.proxy.socks_port and network.proxy.socks_version to 5. Firefox then sends the domain name to the proxy, because network.proxy.socks5_remote_dns is on by default; the older network.proxy.socks_remote_dns, still found in many tutorials, made no difference for SOCKS5 in Firefox 156, while setting socks5_remote_dns to False made Firefox resolve names locally. Firefox has no preference for proxy credentials and the CDP helper does not apply to it. Selenium's BiDi add_auth_handler did send our credentials, but the page load still failed with UnexpectedAlertPresentException for the proxy login prompt, so use an IP whitelist.

Can Selenium use a SOCKS5 proxy?

Yes, without a password: --proxy-server=socks5://host:port. In our test Chrome sent the domain name to the proxy instead of resolving it locally; Chromium's documentation confirms that with SOCKS5, name resolution always happens on the proxy side.

With a password, no: Chromium supports no SOCKS5 authentication, and driver.get() raised net::ERR_SOCKS_CONNECTION_FAILED. The CDP helper cannot help, because SOCKS has no 407 to answer. Use the SOCKS5 port with a whitelist, or the HTTP port with the helper. More in What Is a SOCKS5 Proxy? and SOCKS vs. HTTP Proxy; Proxynet's SOCKS5 Proxy runs on its own port of the same gateway.

Does Selenium work with HTTPS sites through a proxy?

Yes. The proxy address keeps the http:// scheme even when every page is https://: the browser opens a CONNECT tunnel and TLS runs end to end between the browser and the site, so the proxy sees the host name but not the content. That is why a Proxy object needs sslProxy as well as httpProxy; with only httpProxy, our https:// pages went out directly.

An https:// scheme in --proxy-server encrypts the connection to the proxy itself; see HTTPS Proxy.

How do you integrate proxies with Selenium using Java?

Java uses the same Proxy object. The earlier example here put user:pass@ into setHttpProxy, which in our test made Chrome skip the proxy, so the corrected version relies on an IP whitelist. It also drops System.setProperty("webdriver.chrome.driver", …), because Selenium Manager finds the driver. Selenium 4 needs Java 11 or later.

java
import org.openqa.selenium.Proxy;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;

public class SeleniumProxyExample {
    public static void main(String[] args) {
        // No credentials here: the machine's IP is on the proxy's whitelist
        String proxyAddress = "pr.proxynet.io:8000";

        Proxy proxy = new Proxy();
        proxy.setHttpProxy(proxyAddress);
        proxy.setSslProxy(proxyAddress);

        ChromeOptions options = new ChromeOptions();
        options.setProxy(proxy);

        // Selenium Manager finds or downloads the matching ChromeDriver
        WebDriver driver = new ChromeDriver(options);
        try {
            driver.get("https://api.ipify.org/?format=json");
            System.out.println(driver.getPageSource());
        } finally {
            driver.quit();
        }
    }
}

options.addArguments("--proxy-server=http://pr.proxynet.io:8000") is the equivalent of the Python argument. We wrote this example from Selenium's documentation and did not run it, because the test machine has only Java 8.

How do you rotate residential proxies in Selenium?

The proxy is fixed for the life of a browser, so rotation means starting a new browser:

  • Rotating gateway. With Rotating Proxy the exit IP changes on the gateway side and your code keeps one address. A page load's connections can leave from different IPs: fine for product lists, a problem for a login flow.
  • Sticky sessions. With Sticky Proxy the IP stays the same for 1 to 60 minutes. Give each browser its own session username from the Endpoint Generator: one browser, one session, one exit IP.

Both work with Residential Proxy IPs; see What Is IP Rotation? and How to Rotate Proxies in Python.

Full example: parallel browsers with retries

The script reads six JavaScript-rendered pages of quotes.toscrape.com, a practice site. It runs three Chrome instances at once, starts a fresh browser on every attempt, blocks images to save traffic and retries temporary failures with backoff. Errors a retry cannot fix stop the task at once, including those that only show Chrome's error page.

python
import random
import re
import time
from concurrent.futures import ThreadPoolExecutor, as_completed

from selenium import webdriver
from selenium.common.exceptions import TimeoutException, WebDriverException
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait

from proxy_auth import attach_proxy_auth

PROXY_SERVER = "http://pr.proxynet.io:8000"
PROXY_USER = "user"
PROXY_PASS = "pass"
URLS = [f"https://quotes.toscrape.com/js/page/{n}/" for n in range(1, 7)]
WORKERS = 3  # browsers open at the same time
ATTEMPTS = 3
# A retry will not fix these: check the address, port and credentials first
FATAL = ("ERR_PROXY_CONNECTION_FAILED", "ERR_NO_SUPPORTED_PROXIES",
         "ERR_SOCKS_CONNECTION_FAILED", "ERR_TOO_MANY_RETRIES")


def new_driver():
    options = webdriver.ChromeOptions()
    options.add_argument(f"--proxy-server={PROXY_SERVER}")
    options.add_argument("--headless")
    # Skip images: less traffic through the proxy
    options.add_experimental_option("prefs", {"profile.managed_default_content_settings.images": 2})
    driver = webdriver.Chrome(options=options)
    driver.set_page_load_timeout(30)
    attach_proxy_auth(driver, PROXY_USER, PROXY_PASS)
    return driver


def scrape(url):
    for attempt in range(ATTEMPTS):
        driver = new_driver()  # a fresh browser, and a fresh proxy connection, on every attempt
        try:
            driver.get(url)
            # Some proxy errors open Chrome's error page without raising an exception
            if driver.execute_script("return document.body.className") == "neterror":
                code = re.search(r"ERR_[A-Z_]+", driver.page_source)
                raise WebDriverException(code.group(0) if code else "Chrome error page")
            WebDriverWait(driver, 10).until(
                EC.presence_of_element_located((By.CSS_SELECTOR, "div.quote span.text"))
            )
            quotes = driver.find_elements(By.CSS_SELECTOR, "div.quote span.text")
            return url, [q.text for q in quotes]
        except (TimeoutException, WebDriverException) as exc:
            if any(code in str(exc) for code in FATAL) or attempt == ATTEMPTS - 1:
                raise
            time.sleep(2**attempt + random.random())  # exponential backoff with jitter
        finally:
            driver.quit()


with ThreadPoolExecutor(max_workers=WORKERS) as pool:
    futures = [pool.submit(scrape, url) for url in URLS]
    for future in as_completed(futures):
        try:
            url, quotes = future.result()
            print(f"{url}: {len(quotes)} quotes")
        except Exception as exc:
            print("failed:", str(exc).splitlines()[0])

All six pages returned 10 quotes each in about 22 seconds; a closed port stopped every task with net::ERR_PROXY_CONNECTION_FAILED and a wrong password with ERR_TOO_MANY_RETRIES. Keep the worker count modest: each worker is a full Chrome, and too many parallel sessions lead to 429 Too Many Requests.

How do you check that Selenium really uses the proxy?

Open an IP echo page such as api.ipify.org in the proxied browser and compare it with your own IP; if they match, the traffic skips the proxy. Your dashboard should also show traffic. WebRTC can still reveal the local IP; see WebRTC and DNS Leaks. A full routine is in How to Test a Proxy, and a one-line terminal check in cURL with Proxy.

Common Selenium proxy errors and how to fix them

Messages from our tests. Chrome codes appear in the exception (unknown error: net::ERR_…) or only on the error page.

MessageCauseFix
Blank page, empty driver.title, no exceptionProxy answered 407, no credentials sentWhitelist the IP or use the CDP helper
ERR_NO_SUPPORTED_PROXIES (error page)Credentials inside --proxy-serverRemove user:pass@ from the argument
net::ERR_PROXY_CONNECTION_FAILEDNothing listens at that host and portCheck address, port and firewall
net::ERR_TUNNEL_CONNECTION_FAILEDThe proxy could not open the tunnelCheck the target host, then the proxy
net::ERR_SOCKS_CONNECTION_FAILEDSOCKS5 proxy wants a passwordHTTP port with the helper, or a whitelist
ERR_TOO_MANY_RETRIES (error page)The proxy rejected the helper's credentialsCopy the username and password again
TypeError: WebDriver.__init__() got an unexpected keyword argument 'executable_path'Selenium 3 codeRemove it; Selenium Manager finds the driver
… unexpected keyword argument 'chrome_options'Same, older nameUse options=
Timed out receiving message from rendererA page load never finishedCheck the proxy, then retry

Connection errors from plain Python HTTP clients are read line by line in Max Retries Exceeded With URL.

Use cases

A proxy changes where the request comes from, not what the site allows: read its robots.txt and terms, keep request rates low and prefer an official API. It also does not hide automation; a browser under WebDriver control reports navigator.webdriver as true, as the standard requires.

Decision guide

NeedRecommendation
Server with a fixed IPIP whitelist + --proxy-server, no password in code
Laptop or CI runner with a changing IPHTTP port + the CDP helper
FirefoxNetwork preferences + IP whitelist
SOCKS5socks5:// in --proxy-server, whitelist only
Login or checkout flowSticky session, one browser per session
Many independent pagesRotating gateway
A different proxy per page in one browserPlaywright's per-context proxy, see Playwright with a proxy

Frequently asked questions

Can Selenium use a proxy with a username and password?

Not through the standard options, which have no credential fields. In Chrome and Edge the DevTools helper in this post answers the proxy's 407; in any browser an IP whitelist removes the need for a password.

Does Selenium Wire still work in 2026?

It was archived on 3 January 2024, and in a fresh Python 3.13 environment it did not import for us, because it relies on pkg_resources, which current setuptools no longer ships. Use the helper or a whitelist.

How do I change the proxy without restarting the browser?

The proxy is fixed when the session starts: call driver.quit() and start a new driver. For a new IP per page inside one browser, use a rotating gateway, or Playwright, which takes a proxy per context.

Does a proxy work in headless mode?

Yes. --proxy-server and the CDP helper behave the same with and without --headless; all examples in this post ran headless.

Can I use a SOCKS5 proxy with authentication in Selenium?

Not in Chrome or Edge, which support no SOCKS5 authentication; Firefox 156 also offered our proxy only the no-password method. Use SOCKS5 with a whitelist, or the HTTP port with a password.

Why does the site still show my real IP?

Usually the proxy was never applied: credentials inside the Proxy object make Chrome connect directly, and a Proxy object without sslProxy leaves https:// pages unproxied. If the IP is right but the location looks wrong, test for WebRTC leaks.

Summary

Selenium sets the proxy once per browser session: --proxy-server for Chrome and Edge, preferences or the Proxy object for Firefox. For authentication, use an IP whitelist or, in Chrome and Edge, the DevTools helper that answers the 407; Selenium Wire and command-line extensions are dead ends. SOCKS5 works only without a password, and rotation means one browser per IP or session. When the setup is ready, pick a plan on the Proxynet proxy page and take the gateway details from the Endpoint Generator.

Ask ChatGPTAsk Claude