Save 5% every month: use code 5OFFSTORM at checkout
Home / Guides / Selenium

How to use a proxy with Selenium in Python (Chrome and Firefox)

For Chrome, add --proxy-server=http://GATEWAY_IP:PORT to ChromeOptions. For Firefox, set the network.proxy.* preferences or assign a Selenium Proxy object to options.proxy. Selenium has no clean way to send a proxy username and password, so authorize your IP and skip credentials altogether.

Updated October 2026Code & scraping frameworks

Selenium is the oldest of the browser automation tools and still the most widely deployed: QA teams run it on Grid, and scrapers use it when a site needs a real browser and the codebase already speaks WebDriver. Proxy setup depends on the browser. Chrome reads a command-line flag; Firefox reads its own preferences; Selenium’s Proxy class wraps both as a WebDriver capability.

The setup itself takes three lines. Authentication is where people lose hours. Neither the Chrome flag nor the WebDriver proxy capability carries a username and password, so a proxy that asks for them pops up a login dialog that the script can’t fill. The old workaround, the selenium-wire package, is no longer maintained.

This guide shows working Chrome and Firefox setups in Python, explains why IP authorization is the practical answer to the credential problem, covers headless mode, Selenium Grid and Selenium Manager, works out how many drivers your plan carries, and lists the errors with fixes.

Which Storm plan fits

Selenium fits rotating proxies best when it’s scraping: one gateway in the options, a new IP per connection, unlimited bandwidth. Each browser window uses around 10 threads, so a 40-thread plan runs about 4 drivers at once; the 10-thread package is for testing only. For one fixed IP per account, a private dedicated proxy with IP authorization is the simplest match.

Rotating proxies (from $14/mo): 700,000+ IPs behind fixed gateway IP:PORTs. New IP on every request, or every 3 or 15 minutes. USA, EU, USA+EU or Worldwide. Unlimited bandwidth on every plan.

Get 40 threads for $39/mo See all rotating proxies plans

Before you start

  1. Log in to the member area and copy your gateway IP:PORTs. They never change; the rotation happens on our side.
  2. Add the public IP of the computer or server that will run your tool under Authorized IPs, click Save, and allow up to 15 minutes before testing. Rotating and residential proxies use IP authentication, so there is no username or password.
  3. Dedicated proxies work with either IP authentication or a username and password. Use user:pass if your IP changes or the tool runs on several machines.
  4. Count your threads: the tool’s total open connections must stay within your plan (for example 40 threads on the 40-thread plan).

Set up a Selenium proxy, step by step

  1. Install Selenium 4

    Run pip install selenium. Selenium Manager, built in since 4.6, finds or downloads the matching chromedriver or geckodriver for you, so you no longer manage driver binaries by hand.

  2. Authorize your IP first

    Because Selenium can’t answer a proxy login cleanly, use IP authorization: save the public IP of the machine that runs the browser under Authorized IPs in the member area and wait up to 15 minutes. Rotating and residential plans work only this way; dedicated proxies support it too.

  3. Chrome: add the proxy flag

    Create ChromeOptions() and call options.add_argument("--proxy-server=http://GATEWAY_IP:PORT"). Host and port only. Then start the driver with webdriver.Chrome(options=options).

  4. Firefox: set the proxy preferences

    Create FirefoxOptions() and set network.proxy.type to 1 (manual), then network.proxy.http/http_port and network.proxy.ssl/ssl_port to the gateway. Or use the cross-browser Proxy object shown below.

  5. Confirm the exit IP

    Open https://httpbin.org/ip with driver.get() and read driver.find_element(By.TAG_NAME, "body").text. A pool IP means the proxy is active. If the page loads but shows your address, the option never reached the browser.

  6. Set timeouts and close drivers

    Call driver.set_page_load_timeout(60) so a stalled page raises an error instead of hanging. Always end with driver.quit() in a finally block; an orphaned browser keeps its proxy connections open.

Selenium proxy code for Python

Use a gateway from your member area in place of GATEWAY_IP:PORT. With your IP authorized, none of these need a username or password.

Chrome with --proxy-server
from selenium import webdriver
from selenium.webdriver.common.by import By

GATEWAY = "GATEWAY_IP:PORT"

options = webdriver.ChromeOptions()
options.add_argument(f"--proxy-server=http://{GATEWAY}")
options.add_argument("--headless=new")            # remove to watch the browser
options.add_argument("--window-size=1366,900")

driver = webdriver.Chrome(options=options)
try:
    driver.set_page_load_timeout(60)
    driver.get("https://httpbin.org/ip")
    print(driver.find_element(By.TAG_NAME, "body").text)
finally:
    driver.quit()
Firefox with proxy preferences
from selenium import webdriver
from selenium.webdriver.common.by import By

HOST, PORT = "GATEWAY_IP", 8080          # use your gateway's IP and port (PORT is an int)

options = webdriver.FirefoxOptions()
options.set_preference("network.proxy.type", 1)          # 1 = manual configuration
options.set_preference("network.proxy.http", HOST)
options.set_preference("network.proxy.http_port", PORT)
options.set_preference("network.proxy.ssl", HOST)         # used for https sites
options.set_preference("network.proxy.ssl_port", PORT)
options.add_argument("-headless")

driver = webdriver.Firefox(options=options)
try:
    driver.get("https://httpbin.org/ip")
    print(driver.find_element(By.TAG_NAME, "body").text)
finally:
    driver.quit()
Cross-browser: Selenium's Proxy object
from selenium import webdriver
from selenium.webdriver.common.proxy import Proxy, ProxyType

proxy = Proxy({
    "proxyType": ProxyType.MANUAL,
    "httpProxy": "GATEWAY_IP:PORT",     # host:port, no scheme
    "sslProxy": "GATEWAY_IP:PORT",
})

options = webdriver.ChromeOptions()     # or webdriver.FirefoxOptions()
options.proxy = proxy
driver = webdriver.Chrome(options=options)
Several drivers in parallel, sized to the plan
from concurrent.futures import ThreadPoolExecutor
from selenium import webdriver
from selenium.webdriver.common.by import By

PLAN_THREADS = 40
DRIVERS = PLAN_THREADS // 10                      # ~10 threads per browser window
GATEWAY = "http://GATEWAY_IP:PORT"

def make_driver():
    o = webdriver.ChromeOptions()
    o.add_argument(f"--proxy-server={GATEWAY}")
    o.add_argument("--headless=new")
    o.add_argument("--blink-settings=imagesEnabled=false")   # fewer requests per page
    d = webdriver.Chrome(options=o)
    d.set_page_load_timeout(60)
    return d

def crawl(batch):
    d = make_driver()
    try:
        for url in batch:
            try:
                d.get(url)
                print(url, d.title)
            except Exception as e:
                print(url, type(e).__name__)
    finally:
        d.quit()

urls = [f"https://httpbin.org/anything/{i}" for i in range(40)]
batches = [urls[i::DRIVERS] for i in range(DRIVERS)]
with ThreadPoolExecutor(max_workers=DRIVERS) as pool:
    list(pool.map(crawl, batches))
Remote WebDriver / Selenium Grid
options = webdriver.ChromeOptions()
options.add_argument("--proxy-server=http://GATEWAY_IP:PORT")
driver = webdriver.Remote(command_executor="http://GRID_HOST:4444", options=options)
# The browser runs on the Grid node: authorize the node's public IP, not your laptop's.

Why username and password are awkward in Selenium

Chrome’s --proxy-server flag accepts a scheme, host and port, nothing more. The WebDriver Proxy capability has the same shape. When a proxy answers with “407 Proxy Authentication Required”, the browser shows its own sign-in dialog. That dialog isn’t part of the web page, so find_element can’t see it and the script waits until it times out.

The common workarounds all have a cost:

  • selenium-wire put a local proxy between the browser and yours and injected the credentials. Its repository was archived on 3 January 2024 and its README says it’s no longer maintained, so new projects shouldn’t depend on it.
  • A small browser extension that answers the auth challenge works in Chrome and Firefox but has to be built, packed and loaded per proxy, and it adds moving parts to every run.
  • A local forwarding proxy that adds credentials before passing traffic on works, but it’s one more process to run and monitor.

IP authorization removes the problem. The gateway checks the IP you connect from, so the browser never sees a 407 and nothing needs to be typed. All Storm rotating and residential plans work this way, and private dedicated proxies let you choose IP authorization instead of user:pass. The IP authentication guide explains the member-area side in more detail.

Chrome flag, Firefox prefs or the Proxy object

All three end in the same place: the browser sends traffic to the gateway. Pick by what your code already does.

  • --proxy-server is the shortest for Chrome and Edge, and it accepts --proxy-bypass-list next to it for hosts that should go direct.
  • Firefox preferences give you full control, including network.proxy.no_proxies_on for bypass hosts. Set both the http and ssl pairs; with only the http pair, https sites go out directly from your own IP.
  • The Proxy object is browser-neutral, so the same code runs on Chrome and Firefox and travels to a Grid as a capability. Its values are host:port without http://.

One side effect worth knowing: when you set a proxy in the options, Selenium Manager also uses it to download drivers and browsers. On a fresh machine whose IP isn’t authorized yet, that first download can fail through the gateway. Authorize the IP before the first run, or give Selenium Manager its own setting with the SE_PROXY environment variable.

How many browsers your plan carries

Each Selenium driver is a full browser. A single page load pulls HTML, scripts, styles, images, fonts and API calls over several connections at once, so count around 10 threads per open window or tab.

  • Rotating: 10 threads = 1 driver for testing, 40 = 4 drivers, 80 = 8, 150 = 15, 200 = 20.
  • Residential: one port carries up to 50 threads, roughly 5 drivers, all sharing that port’s current IP. Need 5 drivers on 5 different IPs? Give each driver its own port.
  • Dedicated: up to 100 threads per proxy, roughly 10 drivers on one static IP; one proxy per driver when each needs its own IP.

Disabling images (the blink-settings flag in Chrome, or permissions.default.image set to 2 in Firefox) cuts requests per page and lets the same plan carry more drivers. Search engine scraping should stay within 25% of a rotating plan and go through the Main gateway.

Changing IPs between pages

A rotating gateway picks the exit IP for each new connection, but a browser keeps its connections open, so consecutive driver.get() calls to one site often leave from the same IP. To get a different IP for each job, quit the driver and start a new one; a new browser opens new connections. Restarting is slower than reusing, so batch several URLs per driver when the IP doesn’t need to change.

When a test or flow needs one IP for its whole duration, such as signing in and then working inside the account, use the 15-minute gateway or a residential port. Residential IPs switch on a fixed schedule (minute 1, 6, 11, 16 and onward each hour), so keep each flow short or retry a step that fails exactly at the switch.

Selenium Grid, Docker and CI runners

With webdriver.Remote the browser runs on the Grid node, so the IP that reaches the gateway is the node’s, not the one running your Python. Authorize each node’s public IP. In Docker images on a VPS, that’s usually the host’s IP. Cloud testing services and hosted CI runners leave from changing IPs, which IP-authorized gateways won’t accept reliably; run the browsers on a fixed-IP server, or use dedicated proxies there.

Common errors and fixes

Chrome shows a “Sign in” proxy dialog and the script hangsThe proxy wants credentials Selenium can’t type. On dedicated, switch the proxy to IP authorization in the member area. Rotating and residential gateways never ask for credentials (an unauthorized IP gives a connection error instead), so there the dialog comes from another proxy in the chain. See the 407 guide.
ERR_PROXY_CONNECTION_FAILEDThe browser can’t reach the gateway: a typo in IP or port, or a firewall blocking the outbound port. Copy the gateway again and retry.
ERR_TUNNEL_CONNECTION_FAILEDThe HTTPS tunnel wasn’t opened: an unauthorized IP, which the gateway answers by resetting the connection (wait 15 minutes after saving), or more drivers than your threads allow. See this guide.
TimeoutException on driver.get()The page didn’t load within set_page_load_timeout. Allow 60 seconds, block images, and lower the number of parallel drivers.
https pages show your real IP in FirefoxOnly the network.proxy.http pair is set. Add network.proxy.ssl and ssl_port, or use the Proxy object with sslProxy.
SessionNotCreatedException or driver download fails on first runSelenium Manager tried to fetch the driver through a proxy that doesn’t accept your IP yet. Authorize the IP, or set SE_PROXY separately.
Proxy setting ignored with user:pass@ in the flagChrome doesn’t support credentials in --proxy-server. Remove them and use IP authorization.

FAQ

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

There’s no built-in option for it. Extensions and local forwarding proxies work but add complexity, and selenium-wire is archived. The clean route is IP authorization: with Storm, rotating and residential plans use it by default and dedicated proxies offer it as an alternative to user:pass.

How many Selenium browsers can I run on a Storm 40-thread plan?

About 4 at once, counting roughly 10 threads per browser. Turning images off stretches that a little. For 10 or more parallel browsers, look at the 150- or 200-thread plans.

Is selenium-wire still a good option?

It was archived on 3 January 2024 and isn’t maintained, so it won’t get fixes for new browser or Python versions. Prefer IP authorization or a supported alternative for new work.

How do I rotate proxies in Selenium?

Use a rotating gateway and start a new driver per job; each new browser opens fresh connections and gets a new IP. You don’t need to cycle through a proxy list in your code.

Does the proxy work in headless mode?

Yes. --headless=new in Chrome and -headless in Firefox use the same proxy settings as a visible window. Debug with the window visible, then switch to headless.

Can I use Storm proxies with Selenium on BrowserStack or another cloud grid?

Rotating and residential gateways need a fixed authorized IP, which cloud grids usually don’t provide. Run your own Grid on a fixed-IP server, or use private dedicated proxies there.

Still have questions? Contact us here. A real person answers.

Related guides

Tool facts checked against the official documentation (October 2026): Selenium: Browser options (proxy) · Selenium: Firefox specific functionality · Selenium Manager (proxy, SE_PROXY) · selenium-wire repository (archived) · Chromium: network settings and proxy flags. Storm Proxies facts: our plans page and refund policy.

Unlimited bandwidth. One flat monthly price.

Access is live the moment you pay, and the smallest package of each proxy type has a 24-hour money-back guarantee on your first order.