Authenticated & rotating proxies made easy for Chrome-based browser automation
— Selenium, SeleniumBase,
undetected-chromedriver,
Playwright, or plain requests.
Zero third-party dependencies. Standard library only.
Proxies are a constant source of pain in browser automation:
| Problem | Chrome / Selenium out of the box | With selenium-proxy-tools |
|---|---|---|
| Authenticated SOCKS5/SOCKS4 | ❌ impossible — Chrome can't auth SOCKS at all | ✅ |
| Authenticated HTTP/HTTPS | ✅ | |
| Rotating pool / round-robin | ❌ roll your own | ✅ built in |
| DNS leaks | ✅ remote DNS via SOCKS5 |
It works by running a tiny local, no-auth HTTP proxy on a background thread that forwards every connection through your real upstream proxy. You just point the browser at the local address — no flags, no extensions, no external tools.
Chrome ──▶ 127.0.0.1:<port> (local, no auth)
│
▼
your authenticated / rotating upstream proxy ──▶ the internet
pip install selenium-proxy-toolsfromseleniumbaseimportSBfromselenium_proxy_toolsimportProxyRelaywithProxyRelay("socks5://user:pass@host:1080") asrelay:
# relay.proxy -> "127.0.0.1:<port>" (exactly what SeleniumBase expects)withSB(uc=True, proxy=relay.proxy) assb:
sb.open("https://api.ipify.org?format=json")
print(sb.get_text("body"))importundetected_chromedriverasucfromselenium_proxy_toolsimportProxyRelay# Works the same for an authenticated HTTP proxy: "http://user:pass@host:8080"withProxyRelay("socks5://user:pass@host:1080") asrelay:
options=uc.ChromeOptions()
options.add_argument(f"--proxy-server={relay.url}") # http://127.0.0.1:<port>driver=uc.Chrome(options=options)
driver.get("https://api.ipify.org?format=json")
print(driver.find_element("tag name", "body").text)
driver.quit()fromselenium_proxy_toolsimportProxyRelaypool= [
"socks5://user:pass@a.example.com:1080",
"http://user:pass@b.example.com:8080", # mix schemes freely"socks5://user:pass@c.example.com:1080",
]
withProxyRelay(pool, rotation="roundrobin") asrelay: # or rotation="random"withSB(uc=True, proxy=relay.proxy) assb:
... # each new connection exits through the next upstreamimportrequestsfromselenium_proxy_toolsimportProxyRelaywithProxyRelay("http://user:pass@host:8080") asrelay:
print(requests.get("https://api.ipify.org", proxies={"https": relay.url}).text)
print("exit IP:", relay.exit_ip())More runnable examples in examples/.
| Scheme | Auth | Notes |
|---|---|---|
socks5:// | ✅ user/pass (RFC 1929) | remote DNS (no leaks) |
socks4:// | ✅ userid | socks4a remote DNS |
http:// / https:// | ✅ Basic | CONNECT + absolute-form |
Accepted formats: scheme://user:pass@host:port, scheme://host:port,
host:port:user:pass (assumes SOCKS5), host:port.
upstream is one proxy or a list of them. rotation is None, "roundrobin",
or "random".
| Member | Description |
|---|---|
.start(timeout=10.0) | Start; blocks until bound. Returns self. |
.stop() | Stop and release the local port. |
.proxy | "127.0.0.1:<port>" — for SeleniumBase / uc proxy=. |
.url | "http://127.0.0.1:<port>" — for --proxy-server=, requests, Playwright. |
.port | The chosen local port (None until started). |
.exit_ip(timeout=20) | The public IP currently seen through the relay. |
| context manager | with ProxyRelay(...) as relay: starts on enter, stops on exit. |
SocksProxyRelay is a backwards-compatible alias of ProxyRelay.
Also exported: parse_upstream(str) -> UpstreamProxy and
get_exit_ip(proxy, timeout=20).
Manual lifecycle (reuse one relay across many browsers):
relay=ProxyRelay("socks5://user:pass@host:1080").start()
try:
... # use relay.proxy / relay.url as many times as you likefinally:
relay.stop()- Binds a local
asyncioTCP server on127.0.0.1(ephemeral port by default), running its own event loop on a daemon thread — so it plays nicely inside ordinary synchronous Selenium code. - Speaks the HTTP proxy protocol locally:
CONNECTfor HTTPS tunnels, absolute-form for plain HTTP. - Opens each connection through the selected upstream: SOCKS5 (user/pass),
SOCKS4/4a, or HTTP/HTTPS (
CONNECT+Proxy-Authorization), then pipes bytes both ways.
Does the local proxy need a password? No — it listens on loopback only and isn't reachable from outside your machine.
HTTP and HTTPS both work? Yes. HTTPS goes through CONNECT (end-to-end
encrypted; the relay only tunnels bytes).
Do I need pysocks, pproxy, or anything else? No — standard library only.
Rotating residential proxies? Either give a pool and use rotation=, or
if your provider rotates behind a fixed host:port, just keep one relay — each
connection exits via whatever IP the upstream currently serves.
git clone https://github.com/SAKMZ/selenium-proxy-tools
cd selenium-proxy-tools
python -m unittest discover -s tests -vThe suite spins up local SOCKS5-auth and HTTP-auth proxy servers, so the full relay path (auth + rotation) is exercised without touching the network.
MIT © Saif Ali (SAKMZ)