Skip to content

Repository files navigation

selenium-proxy-tools

testsPyPIPythonLicense: MIT

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.


Why?

Proxies are a constant source of pain in browser automation:

ProblemChrome / Selenium out of the boxWith selenium-proxy-tools
Authenticated SOCKS5/SOCKS4❌ impossible — Chrome can't auth SOCKS at all
Authenticated HTTP/HTTPS⚠️ needs a hand-built MV3 extension per session
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

Install

pip install selenium-proxy-tools

Quick start

SeleniumBase

fromseleniumbaseimportSBfromselenium_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"))

undetected-chromedriver / plain Selenium

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()

Rotating pool

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 upstream

requests / verify the exit IP

importrequestsfromselenium_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/.

Supported upstreams

SchemeAuthNotes
socks5://✅ user/pass (RFC 1929)remote DNS (no leaks)
socks4://✅ useridsocks4a remote DNS
http:// / https://✅ BasicCONNECT + absolute-form

Accepted formats: scheme://user:pass@host:port, scheme://host:port, host:port:user:pass (assumes SOCKS5), host:port.

API

ProxyRelay(upstream, listen_host="127.0.0.1", listen_port=0, rotation=None)

upstream is one proxy or a list of them. rotation is None, "roundrobin", or "random".

MemberDescription
.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.
.portThe chosen local port (None until started).
.exit_ip(timeout=20)The public IP currently seen through the relay.
context managerwith 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()

How it works

  • Binds a local asyncio TCP server on 127.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: CONNECT for 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.

FAQ

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.

Development

git clone https://github.com/SAKMZ/selenium-proxy-tools
cd selenium-proxy-tools
python -m unittest discover -s tests -v

The suite spins up local SOCKS5-auth and HTTP-auth proxy servers, so the full relay path (auth + rotation) is exercised without touching the network.

License

MIT © Saif Ali (SAKMZ)

About

Authenticated & rotating proxies for Chrome-based Selenium, SeleniumBase, undetected-chromedriver & Playwright. Handles authed SOCKS5/SOCKS4 + HTTP/HTTPS and proxy pools via a local relay.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages