Free proxies for Selenium work for a throwaway, attended session and fail an unattended run. A fresh free proxy is fine when you want to check how a page renders from another country or confirm your automation routes traffic at all, because if the exit dies you just rerun it. It falls apart the moment the job has to run on its own, because Selenium has no built-in per-request rotation the way a crawler does: a dead free exit hangs the page load until it times out, and swapping to a new IP means quitting the driver and launching a whole new browser. Free is fine for a look, not for a loop.
We run a proxy network and a live proxy checker, so we see the same Selenium pattern constantly: a script that works in a demo against one hand-picked proxy, then stalls overnight because the free pool underneath it churned. This covers how to set a proxy in Selenium the right way, why authenticated proxies need more than the command-line flag, why a free pool is brutal specifically for a browser, and how to verify the exit. For the paid-tier choices per target, see proxies for Selenium.
Setting a proxy with the Chrome flag
The simplest way to point Selenium's Chrome at a proxy is a launch argument. This is all you need for an open proxy, which nearly every free entry is:
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
options = Options()
options.add_argument("--proxy-server=http://45.61.10.20:8080")
driver = webdriver.Chrome(options=options)
driver.get("https://httpbin.org/ip")
print(driver.page_source) # should show the proxy's IP, not yours
driver.quit()
One limit trips everyone. The --proxy-server flag takes host and port only. Embed user:pass@ in it and Chrome discards the credentials and throws up a proxy authentication dialog that a headless script cannot click, so the request hangs. Free list entries are usually open, so the flag is enough for them, but a paid gateway with a username and password needs a different tool.
Authenticated proxies need Selenium Wire
For any proxy that requires a login, the common answer is Selenium Wire, a drop-in extension of Selenium that also lets you set the proxy (with credentials) through a config dict and inspect requests:
from seleniumwire import webdriver # pip install selenium-wire
seleniumwire_options = {
"proxy": {
"http": "http://user:pass@gateway.hproxy.com:PORT",
"https": "http://user:pass@gateway.hproxy.com:PORT",
"no_proxy": "localhost,127.0.0.1",
}
}
driver = webdriver.Chrome(seleniumwire_options=seleniumwire_options)
driver.get("https://httpbin.org/ip")
Selenium Wire handles the Proxy-Authorization the flag cannot, which is why it, or a small credentials extension you load into Chrome, is the standard route for a paid gateway. For a free open proxy you do not need it, and the guide how to use proxies in Selenium walks through both paths in more depth.
Firefox instead of Chrome
The same job in Firefox uses proxy preferences rather than a launch flag, which keeps the proxy inside the profile:
from selenium import webdriver
from selenium.webdriver.firefox.options import Options as FirefoxOptions
options = FirefoxOptions()
options.set_preference("network.proxy.type", 1) # 1 = manual config
options.set_preference("network.proxy.http", "45.61.10.20")
options.set_preference("network.proxy.http_port", 8080)
options.set_preference("network.proxy.ssl", "45.61.10.20")
options.set_preference("network.proxy.ssl_port", 8080)
driver = webdriver.Firefox(options=options)
driver.get("https://httpbin.org/ip")
Firefox has the same authentication gap as Chrome's flag: these preferences take a host and port and no credentials, so an authenticated proxy still needs Selenium Wire. Note also that the old Proxy capability class is deprecated in Selenium 4, so set the proxy through browser options on either engine rather than the desired-capabilities pattern you will still find in older answers online.
Why a free pool is brutal for Selenium specifically
A crawler like Scrapy swaps meta["proxy"] per request for free, so rotating through a dead pool costs almost nothing. Selenium does not work that way. The proxy is bound when the browser launches, so changing the exit means tearing the browser down and building a new one.
Free exit dies
it expired minutes ago
Page load times out
no per-request rotation to fall back on
Quit the driver
the proxy was fixed at launch
Launch a new browser
slow and memory-heavy, per IP
That is the whole problem in one picture. With a pool where only a few thousand of the hundreds of thousands we track are alive at any instant, a Selenium loop spends its time relaunching Chrome, and each relaunch is seconds and hundreds of megabytes of RAM. The mitigation is to fail fast and rebuild deliberately: set a page load timeout so a dead exit does not hang, and recreate the driver on the next entry.
from selenium.common.exceptions import TimeoutException, WebDriverException
def make_driver(proxy):
options = Options()
options.add_argument(f"--proxy-server=http://{proxy}")
driver = webdriver.Chrome(options=options)
driver.set_page_load_timeout(20) # a dead proxy must not hang the run
return driver
for proxy in pool:
driver = make_driver(proxy)
try:
driver.get("https://example.com")
break # this exit worked, carry on
except (TimeoutException, WebDriverException):
driver.quit() # tear it down and try the next IP
continue
This survives a free pool for a test. It does not make one fast, because the cost is the browser rebuild, not the code.
There is a deeper reason this stings more in a browser than in a script. A headless Chrome is a full rendering engine, so every relaunch pays for a fresh process, a new profile directory, and the memory that comes with them. Doing that once per dead exit turns a churny free pool into a machine that spends more time starting browsers than loading pages. A crawler pays a few cents of overhead for the same churn, because it swaps a string and moves on. A browser pays seconds and hundreds of megabytes each time, which is why the free-pool tax that is merely annoying in Scrapy is close to disqualifying in Selenium.
Verify the exit, and check WebRTC
Never trust a session because the browser opened. The first thing to confirm is that the target sees the proxy and not you:
driver.get("https://httpbin.org/ip")
print(driver.page_source) # the proxy's address, or the run is leaking
Do the same check outside the browser with our free proxy checker, which reports the real exit IP, country, latency, and anonymity grade so you can drop anything transparent, and how to check if a proxy is working covers the full method. One trap is specific to browsers: a proxy set on Chrome routes HTTP traffic, but plain Selenium does not mask WebRTC, so a page's JavaScript can read your real IP over a STUN request even with the proxy active. Selenium has no clean built-in fix for that, which is one of the reasons multi-account work moves to an antidetect browser (the free-versus-paid version of that story is in free proxies for Multilogin).
Never put a login behind a free proxy
The reachability check is the safe use of a free proxy. A login is not. When Selenium drives an authenticated flow, it sends cookies and often credentials, and a free proxy is run by a stranger sitting in the middle of that connection. Traffic to an HTTPS site stays encrypted across the hop, but plenty of free proxies are transparent, misconfigured, or hostile, and some tamper with anything that is not. Keep free proxies to logged-out page pulls and geo checks, and read are free proxies safe for the full trust picture before you automate anything that signs in. The moment a script has to log in and stay logged in, you have left the territory a free proxy belongs in.
When to switch to a gateway
Free proxies end where the unattended run begins. The fix for the browser-rebuild problem is a rotating residential gateway: one endpoint, with credentials handled by Selenium Wire, that rotates the exit server-side, so you launch the browser once and never tear it down to change IP. That removes the single most expensive part of running Selenium behind a pool.
Our residential proxies start at $0.44 per GB, pay as you go, with no KYC and a balance that does not expire, so a script you run in bursts never pays for idle IPs. If your target is better suited to a headless crawler than a full browser, the same free-versus-paid split is in free proxies for Scrapy.
Prove your automation against our free proxy list until the session visibly egresses from another IP, pull a test pool from the free proxy API, and vet any entry in the proxy checker first. When the run has to stand on its own, move it to residential at $0.44/GB behind a gateway, set your timeouts, and let one steady endpoint do the rotation the browser cannot.