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:
- You put the proxy into the browser options, as the Chrome argument
--proxy-serveror as aProxyobject (theproxycapability of the WebDriver standard). - The driver starts the browser with that setting, and it applies to every tab until
driver.quit(). - For an
https://page the browser sendsCONNECT host:443to the proxy and opens an encrypted tunnel. For anhttp://page it sends the full URL to the proxy. - If the proxy needs a login, it answers
407 Proxy Authentication Required. This is the step the WebDriver standard does not cover. - 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:
pip install seleniumStep 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):
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 tried | What happened |
|---|---|
--proxy-server=http://host:port, no credentials | The proxy returned 407; no exception, blank page |
--proxy-server=http://user:pass@host:port | ERR_NO_SUPPORTED_PROXIES error page, no exception |
user:pass@host:port in the Proxy object | The page loaded, but Chrome connected directly, from our own IP |
Selenium BiDi driver.network.add_auth_handler(), Chrome | Answering 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.
# 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 wsUsing it takes one line after the driver starts:
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?
| Method | Chrome / Edge | Firefox | Password | Status in 2026 |
|---|---|---|---|---|
--proxy-server argument | Yes | No, use preferences | No | Current |
Proxy object | Yes | Yes | Ignored | Current |
| IP whitelist | Yes | Yes | Not needed | Current |
| CDP helper | Yes | No | Yes | Tested, works |
BiDi add_auth_handler | Failed in Chrome | Login prompt error | In theory | Not reliable yet |
| Selenium Wire | Import fails | Import fails | Yes | Archived |
| Auth extension | Chrome for Testing only | Separate format | Yes | Broken 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:
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.
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.
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.
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.
| Message | Cause | Fix |
|---|---|---|
Blank page, empty driver.title, no exception | Proxy answered 407, no credentials sent | Whitelist the IP or use the CDP helper |
ERR_NO_SUPPORTED_PROXIES (error page) | Credentials inside --proxy-server | Remove user:pass@ from the argument |
net::ERR_PROXY_CONNECTION_FAILED | Nothing listens at that host and port | Check address, port and firewall |
net::ERR_TUNNEL_CONNECTION_FAILED | The proxy could not open the tunnel | Check the target host, then the proxy |
net::ERR_SOCKS_CONNECTION_FAILED | SOCKS5 proxy wants a password | HTTP port with the helper, or a whitelist |
ERR_TOO_MANY_RETRIES (error page) | The proxy rejected the helper's credentials | Copy the username and password again |
TypeError: WebDriver.__init__() got an unexpected keyword argument 'executable_path' | Selenium 3 code | Remove it; Selenium Manager finds the driver |
… unexpected keyword argument 'chrome_options' | Same, older name | Use options= |
Timed out receiving message from renderer | A page load never finished | Check the proxy, then retry |
Connection errors from plain Python HTTP clients are read line by line in Max Retries Exceeded With URL.
Use cases
- Price monitoring on JavaScript-heavy shops, from the market you sell in: competitor price tracking and price monitoring.
- Localisation and QA: loading your own site from several countries before a release, see app testing.
- Ad verification: checking your ads in the regions you pay for, see ad verification.
- Data collection from pages that need JavaScript: data scraping.
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
| Need | Recommendation |
|---|---|
| Server with a fixed IP | IP whitelist + --proxy-server, no password in code |
| Laptop or CI runner with a changing IP | HTTP port + the CDP helper |
| Firefox | Network preferences + IP whitelist |
| SOCKS5 | socks5:// in --proxy-server, whitelist only |
| Login or checkout flow | Sticky session, one browser per session |
| Many independent pages | Rotating gateway |
| A different proxy per page in one browser | Playwright'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.




