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-toolsfrom seleniumbase import SB
from selenium_proxy_tools import ProxyRelay
with ProxyRelay("socks5://user:pass@host:1080") as relay:
# relay.proxy -> "127.0.0.1:<port>" (exactly what SeleniumBase expects)
with SB(uc=True, proxy=relay.proxy) as sb:
sb.open("https://api.ipify.org?format=json")
print(sb.get_text("body"))import undetected_chromedriver as uc
from selenium_proxy_tools import ProxyRelay
# Works the same for an authenticated HTTP proxy: "http://user:pass@host:8080"
with ProxyRelay("socks5://user:pass@host:1080") as relay:
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()from selenium_proxy_tools import ProxyRelay
pool = [
"socks5://user:pass@a.example.com:1080",
"http://user:pass@b.example.com:8080", # mix schemes freely
"socks5://user:pass@c.example.com:1080",
]
with ProxyRelay(pool, rotation="roundrobin") as relay: # or rotation="random"
with SB(uc=True, proxy=relay.proxy) as sb:
... # each new connection exits through the next upstreamimport requests
from selenium_proxy_tools import ProxyRelay
with ProxyRelay("http://user:pass@host:8080") as relay:
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 like
finally:
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)