# How to Rotate Proxy in Selenium Python

> How to rotate proxy in Selenium Python: per-session driver restarts, a rotating gateway endpoint, and proxy authentication — with code, cadence rules and cost math.

[Home](https://quanticdata.io/)/[Blog](https://quanticdata.io/blog/)/How to Rotate Proxy in Selenium Python

# How to rotate a proxy in Selenium Python (three working methods)

ProxiesJul 29, 2026·9 min read·QuanticData Team

On this page [Why Selenium IP rotation is a process-lifecycle problem](/blog/how-to-rotate-proxy-in-selenium-python/#why-selenium-ip-rotation-is-a-process-lifecycle-problem) [Method 1: a new driver per proxy (works with vanilla Selenium)](/blog/how-to-rotate-proxy-in-selenium-python/#method-1-a-new-driver-per-proxy-works-with-vanilla-selenium) [Method 2: one rotating endpoint, sticky sessions where needed](/blog/how-to-rotate-proxy-in-selenium-python/#method-2-one-rotating-endpoint-sticky-sessions-where-needed) [Method 3: Selenium proxy authentication (Chrome and Firefox)](/blog/how-to-rotate-proxy-in-selenium-python/#method-3-selenium-proxy-authentication-chrome-and-firefox) [Which method to choose](/blog/how-to-rotate-proxy-in-selenium-python/#which-method-to-choose) [Rotation cadence and retry logic that actually helps](/blog/how-to-rotate-proxy-in-selenium-python/#rotation-cadence-and-retry-logic-that-actually-helps) [The honest cost math: rotating browsers vs a scraping API](/blog/how-to-rotate-proxy-in-selenium-python/#the-honest-cost-math-rotating-browsers-vs-a-scraping-api) [When an agent tool replaces the rotation code entirely](/blog/how-to-rotate-proxy-in-selenium-python/#when-an-agent-tool-replaces-the-rotation-code-entirely)

Chrome reads its proxy from a command-line switch when the process starts, so rotating a proxy in Selenium Python means one of three things: relaunch the driver with a new proxy per session, point every session at a rotating gateway that changes the exit IP for you, or intercept traffic locally with selenium-wire. Each has a different failure mode.

## Why Selenium IP rotation is a process-lifecycle problem

The proxy you pass as `--proxy-server=host:port` is applied to the whole browser process at startup. Selenium's own `Proxy` capability lands in the same place: it is serialised into the session-creation payload and handed to the driver before the first navigation. Chromium documents these as network settings read at launch, and there is no supported public API to swap them for a running profile ([Chromium network settings](https://www.chromium.org/developers/design-documents/network-settings/)).

That leaves exactly three levers:

- **New process per IP** — quit the driver, launch a new one with a different proxy string.

- **Stable proxy string, rotating upstream** — the browser keeps one endpoint, and the proxy provider changes the exit IP per request or per session token.

- **Local interception** — a proxy running on `127.0.0.1` (selenium-wire, mitmproxy, or your own forwarder) that re-routes upstream on demand. The browser never sees the change.

Everything you will read about *Selenium proxy Python* rotation is a variation on those three. Pick based on how often you need a new IP and whether you need cookies to survive rotation.

## Method 1: a new driver per proxy (works with vanilla Selenium)

This is the portable option: no extra dependencies, works on Chrome, Firefox and Edge, and it also resets cookies, cache and storage — which is usually what you want when you rotate identity.

```
import itertools
from selenium import webdriver
from selenium.webdriver.common.by import By

PROXIES = [
    "http://198.51.100.7:8000",
    "http://203.0.113.24:8000",
    "http://192.0.2.181:8000",
]

URLS = ["https://httpbin.org/ip"] * 3

def make_driver(proxy: str):
    opts = webdriver.ChromeOptions()
    opts.add_argument("--headless=new")
    opts.add_argument(f"--proxy-server={proxy}")
    # keep the proxy on localhost traffic out of the way
    opts.add_argument("--proxy-bypass-list=<-loopback>")
    return webdriver.Chrome(options=opts)

for url, proxy in zip(URLS, itertools.cycle(PROXIES)):
    driver = make_driver(proxy)
    try:
        driver.set_page_load_timeout(30)
        driver.get(url)
        print(proxy, "->", driver.find_element(By.TAG_NAME, "body").text)
    finally:
        driver.quit()
```

Two costs you should measure before scaling this: browser startup is typically one to three seconds of pure overhead per rotation, and every relaunch throws away warm cache, so the same assets get downloaded again through metered bandwidth. If you rotate every request, most of your proxy spend goes on re-downloading the same CSS and fonts.

## Method 2: one rotating endpoint, sticky sessions where needed

The lower-friction pattern is to give the browser a single proxy string and let the network do the rotation. That is exactly what [rotating proxies](https://quanticdata.io/rotating-proxies/) are for: a fresh IP on every request from one endpoint, or a sticky session held for a defined window when you need cookies and a login to survive. Providers usually encode the choice in the username, so the Python side is just string formatting:

```
import uuid
from selenium import webdriver

USER, PASSWORD, HOST = "your-user", "your-pass", "gateway.example.net:8000"

def sticky_proxy(country="us", minutes=10):
    sid = uuid.uuid4().hex[:8]  # new token = new exit IP
    # username syntax varies by provider - check their docs
    user = f"{USER}-country-{country}-session-{sid}-ttl-{minutes}m"
    return f"http://{user}:{PASSWORD}@{HOST}"

opts = webdriver.ChromeOptions()
opts.add_argument(f"--proxy-server={sticky_proxy()}")  # creds ignored by Chrome, see below
driver = webdriver.Chrome(options=opts)
```

One nuance that trips people up in browser automation: a per-request rotating endpoint will send the HTML, the JS bundle and the XHR calls of a single page load out through *different* IPs. Plenty of sites treat that as an obvious anomaly, and any session cookie issued mid-page breaks. For Selenium, prefer a sticky session for the lifetime of a page or a task, then rotate the token. Per-request rotation belongs to HTTP clients — the pattern we cover in [rotating proxies in plain Python](https://quanticdata.io/blog/how-to-rotate-proxies-python/).

Geo consistency matters too. If you pin a US exit through [residential IPs](https://quanticdata.io/residential-proxies/), keep the browser's timezone, `Accept-Language` and locale aligned with it. An IP in Ohio with a Europe/Berlin timezone is a cheaper signal to detect than the IP itself.

## Method 3: Selenium proxy authentication (Chrome and Firefox)

Chrome discards the `user:pass@` part of `--proxy-server`, so authenticated proxies need one of four approaches.

### IP allowlisting

If your provider supports whitelisting the machine's public IP, use it. No credentials, no extension, no interception — the most reliable option for long-running scrapers on fixed infrastructure.

### A tiny extension that answers the auth prompt

Chrome exposes `chrome.webRequest.onAuthRequired`, so a five-line extension can supply credentials. Generate it at runtime:

```
import json, pathlib, tempfile
from selenium import webdriver

def auth_extension(user, password):
    d = pathlib.Path(tempfile.mkdtemp())
    (d / "manifest.json").write_text(json.dumps({
        "manifest_version": 3,
        "name": "proxy-auth", "version": "1.0",
        "permissions": ["webRequest", "webRequestAuthProvider"],
        "host_permissions": ["<all_urls>"],
        "background": {"service_worker": "bg.js"},
    }))
    (d / "bg.js").write_text(
        "chrome.webRequest.onAuthRequired.addListener("
        f"() => ({{authCredentials: {{username: '{user}', password: '{password}'}}}}),"
        "{urls: ['<all_urls>']}, ['blocking']);"
    )
    return str(d)

opts = webdriver.ChromeOptions()
opts.add_argument("--proxy-server=http://gateway.example.net:8000")
opts.add_argument(f"--load-extension={auth_extension('user', 'pass')}")
driver = webdriver.Chrome(options=opts)
```

Rebuild the extension directory whenever you rotate credentials or session tokens, and remember that extensions require the newer headless mode (`--headless=new`).

### selenium-wire and its maintained alternatives

selenium-wire wraps the driver in a local mitm proxy, which is why it can both authenticate upstream and swap `driver.proxy` between navigations without relaunching the browser. It is also archived — the repository has been read-only since January 2024 ([wkeeling/selenium-wire](https://github.com/wkeeling/selenium-wire)) — and it pins older `blinker` and `pyOpenSSL` versions to work on current Pythons. Use it for short-lived jobs, not for a pipeline you must maintain for a year. SeleniumBase accepts `--proxy=user:pass@host:port` directly and is actively maintained; most *rotating proxy Python GitHub* wrappers you will find are thin layers over one of these two ideas.

### Firefox

For *Selenium Firefox proxy authentication in Python*, set the proxy through profile preferences (`network.proxy.type = 1`, `network.proxy.http`, `network.proxy.ssl`, `network.proxy.http_port`) or `options.proxy`. Firefox then raises a basic-auth dialog that Selenium cannot always dismiss reliably, so the practical fixes are an add-on that handles `onAuthRequired`, or a local credential-injecting forwarder that the browser talks to without auth. The same trick answers *how to set proxy in Selenium WebDriver Java*: build a `org.openqa.selenium.Proxy`, set `setHttpProxy()`, attach it to the options — and handle credentials outside the driver.

## Which method to choose

| Approach | How the IP changes | Auth support | Cost per rotation | Best for |
| --- | --- | --- | --- | --- |
| New driver per proxy | Process relaunch | Extension or allowlist | 1-3s + cold cache | Coarse rotation, one identity per task |
| Rotating gateway endpoint | Upstream, per request or per session token | Credentials in username (needs extension on Chrome) | Near zero | Most scraping and Selenium IP rotation at scale |
| Local interception (selenium-wire) | `driver.proxy` reassigned mid-session | Native | Near zero, plus mitm certificate setup | Ad-hoc jobs, debugging, request inspection |
| Scraping API | Handled by the provider | Bearer key | None | Content extraction where you do not need clicks |

## Rotation cadence and retry logic that actually helps

1. **Rotate on outcome, not on a timer.** A 403, a challenge page or a redirect to a bot-check URL is a rotation signal. A successful 200 is not.

2. **Pin one IP per logical session.** Login, cart and pagination flows need the same exit IP; a new token per *task*, not per request.

3. **Cap retries at three per URL** and change something between attempts — IP, then locale, then rendering strategy. Repeating the identical request is spend without information.

4. **Quarantine bad exits.** If you manage a static list, track failures per proxy and drop it for an hour after two consecutive timeouts.

5. **Block heavy resources.** Images, media and fonts are usually most of the bytes. Blocking them through CDP (`Network.setBlockedURLs`) or `--blink-settings=imagesEnabled=false` is the single biggest cost win in browser scraping.

6. **Log the attempt trail.** Proxy used, status, bytes, wall time. Without it you cannot tell an IP problem from a fingerprint problem — and they need opposite fixes.

## The honest cost math: rotating browsers vs a scraping API

Residential bandwidth is metered, and a headless browser pulls everything a real one does. Take a JS-heavy page that transfers roughly 2.5 MB of assets. At $0.80/GB that is about $0.002 per page in bandwidth alone, before your compute — and blocked attempts consume the same bytes, so a 20% block rate pushes the effective figure closer to $0.0019. Strip images and fonts down to ~400 KB and the same page costs about $0.00023.

Now compare with fetching the same page through the [Web Scraping API](https://quanticdata.io/web-scraping-api/): $0.0002 per page for HTML/Markdown, $0.001 with JS rendering, proxies and retries included, and failures cost nothing under the pay-per-success rule.

```
curl https://api.quanticdata.io/v1/scrape \
  -H "Authorization: Bearer $QD_API_KEY" \
  -d '{ "url": "https://example.com", "render": true }'

{ "success": true,
  "data": { "markdown": "# Example Domain\n..." },
  "usage": { "cost_usd": 0.001 },
  "retries": [ { "attempt": 1, "outcome": "ok" } ] }
```

The point is not that Selenium is wrong — it is that rotation is only cheap when it is coupled to a cost model. Keep the browser for flows that genuinely need clicks, form state or authenticated navigation. Move plain content extraction to a per-success API and your proxy bill stops scaling with retries.

## When an agent tool replaces the rotation code entirely

If the consumer of the data is an LLM or an agent, the whole proxy layer can move behind a tool call. Our [MCP server](https://quanticdata.io/mcp-server/) exposes search, scrape, map, crawl, batch and SEO audit as tools to Claude, Cursor, Windsurf and VS Code, with residential proxies underneath and one JSON envelope out; the same primitives are available as REST through the [Web Data API for AI](https://quanticdata.io/web-data-api-for-ai/). Agents get structured pages without owning a proxy pool, a Chrome install or a rotation policy.

For the cases where a real browser is unavoidable — multi-step checkouts, dashboards behind a login — the trade-off is worth stating plainly: you are paying for a process, not a request. Rotate identities at task boundaries, keep the fingerprint consistent with the exit IP, and instrument every attempt. That combination fixes far more blocks than adding proxies to a list. If you are new to the wider picture, our primer on [browser automation](https://quanticdata.io/blog/what-is-browser-automation/) covers where Selenium fits against CDP-native drivers.

### Sources & further reading

- [Chromium — Network settings (proxy configuration)](https://www.chromium.org/developers/design-documents/network-settings/)

- [selenium-wire (archived repository)](https://github.com/wkeeling/selenium-wire)

- [Selenium documentation — browser options and proxy capability](https://www.selenium.dev/documentation/webdriver/drivers/options/)

- [Stack Overflow — How to rotate Selenium webbrowser IP address](https://stackoverflow.com/questions/59409418/how-to-rotate-selenium-webrowser-ip-address)

## FAQ

Quick answers on how to rotate proxy in selenium python.

[Something else? Ask us →](mailto:hello@quanticdata.io)

### How do I rotate proxies in Selenium Python?

Either relaunch the driver with a different `--proxy-server` value for each identity, or point every session at a rotating proxy endpoint and change the session token to get a new exit IP. selenium-wire is the third option: it lets you reassign `driver.proxy` between page loads without restarting the browser.

### Can Selenium change the proxy without restarting the browser?

Not with vanilla Selenium. Chrome reads the proxy at process launch and offers no supported API to change it mid-session. The workarounds are a local intercepting proxy such as selenium-wire, or a gateway endpoint whose upstream IP changes while the browser's proxy string stays identical.

### Does Selenium support proxy authentication?

Not natively for Chrome — credentials embedded in `--proxy-server` are ignored. Use IP allowlisting where your provider supports it, a small MV3 extension answering `chrome.webRequest.onAuthRequired`, or a wrapper such as selenium-wire or SeleniumBase that handles `user:pass@host:port` for you.

### How do I set a proxy in Selenium WebDriver Java?

Create an `org.openqa.selenium.Proxy`, call `setHttpProxy("host:port")` and `setSslProxy()`, then attach it via `ChromeOptions.setProxy(proxy)`. Authentication is not handled by the driver in Java either, so pair it with IP allowlisting or an auth extension exactly as you would in Python.

### How do I handle Firefox proxy authentication in Selenium Python?

Set `network.proxy.type`, `network.proxy.http` and `network.proxy.ssl` in a Firefox profile or use `options.proxy`. Firefox then prompts for credentials, which Selenium cannot reliably dismiss, so add an add-on that handles the auth event or run a local forwarder that injects the credentials upstream.

### How often should I rotate IPs when scraping with Selenium?

Rotate on failure signals rather than on a fixed interval: blocks, challenge pages and unexpected redirects. Keep one IP for the whole of a logical session so cookies survive, and rotate at task boundaries. Aggressive per-request rotation inside a browser page load often looks more suspicious than a stable IP.

## Rotate IPs without babysitting a proxy list

Point Selenium at one rotating endpoint from $0.50/GB with sticky sessions up to 120 minutes, or skip the browser entirely and fetch pages through the Web Scraping API from $0.0002 each — pay per success, so failed calls cost nothing. $2 of free usage every month, no card required.

[Start free — $2/month included](https://app.quanticdata.io/register)[Explore Rotating Proxies from $0.50/GB](https://quanticdata.io/rotating-proxies/)

## Related reading

[Proxies How to Rotate Proxies in Python Rotate proxies in Python three ways — a cycled list, an async pool with cooldowns, or a rotating gateway — plus the cost math nobody publishes. Read →](https://quanticdata.io/blog/how-to-rotate-proxies-python/) [Proxies How to Use Rotating Proxies Gateway setup, rotation cadence, sticky sessions, retry logic and the real cost math behind rotating proxies — with Python and curl you can run today. Read →](https://quanticdata.io/blog/how-to-use-rotating-proxies/) [SEO data How to Perform an SEO Audit A practical six-step SEO audit process with a checklist, the crawler-vs-user diff most audits skip, and how to run the whole thing programmatically. Read →](https://quanticdata.io/blog/how-to-perform-an-seo-audit/)

---

Source: https://quanticdata.io/blog/how-to-rotate-proxy-in-selenium-python/ · Site index for AI: https://quanticdata.io/llms.txt
