Selenium proxy setup with a login
Chrome's `--proxy-server` flag takes the proxy address but not the login. Selenium can pass the login through a WebDriver BiDi handler or a small extension, without extra packages.
- Protocols
- HTTP proxy on port 7000 (HTTPS sites through CONNECT). Chrome has no SOCKS5 login
- Where the login goes
- A WebDriver BiDi auth handler, or a small extension
- Rotation
- A sticky
-sid-login per driver: one IP for up to 60 minutes - Works with
- Residential and mobile proxies
Before you start
- Selenium 4.34 or newer (
pip install -U selenium); older releases lack the BiDi calls below. Selenium Manager fetches a matching ChromeDriver. - Your proxy login and password from the Residential or Mobile page of the dashboard.
- Traffic on the product you use: a login without traffic gets 402.
- Host
gate.aeonproxy.com- Port (HTTP / HTTPS)
7000- Login
- your proxy login, with targeting keys if you need them:
LOGIN-cc-us-sid-abc123 - Password
- your proxy password
Copy the login and password from the Residential or Mobile page of the dashboard; the generator there also writes ready lines. Key syntax: targeting and sessions.
Why the flag alone is not enough
--proxy-server=http://gate.aeonproxy.com:7000 only tells Chrome where the proxy is. With a login written into that address pages fail with ERR_NO_SUPPORTED_PROXIES; with no login, ERR_INVALID_AUTH_CREDENTIALS. The login has to come from a handler that answers the proxy's 407.
Option 1: the WebDriver BiDi auth handler
Turn BiDi on and register the login once. Selenium then answers the proxy for every page of that driver.
from selenium import webdriver
from selenium.webdriver.common.by import By
options = webdriver.ChromeOptions()
options.add_argument("--proxy-server=http://gate.aeonproxy.com:7000")
options.enable_bidi = True
driver = webdriver.Chrome(options=options)
driver.network.add_auth_handler("LOGIN-cc-us-sid-ab12cd34-ttl-30", "PASSWORD")
# open the first page over BiDi, not with driver.get() (see the note below)
driver.browsing_context.navigate(context=driver.current_window_handle, url="https://api.ipify.org", wait="complete")
print(driver.find_element(By.TAG_NAME, "body").text)
driver.get("https://example.com") # from here on driver.get() works as usual
print(driver.title)
driver.quit()In our test (Selenium 4.51, Chrome 156), a driver.get() as the first page got stuck on the proxy's 407 until the page load timeout, with Timed out waiting for response to BiDi command in the output. browsing_context.navigate worked in every run, and after the first page driver.get() works too: Chrome reuses the login.
add_auth_handler answers every HTTP login prompt, a site's own included. If a site you test uses HTTP authentication, take option 2, which answers only the proxy.
Option 2: a small extension
A Manifest V3 extension with the webRequestAuthProvider permission can answer the proxy's challenge, and only the proxy's (isProxy). Branded Chrome ignores the --load-extension flag since version 137, so let Selenium install the extension:
import json
import pathlib
from selenium import webdriver
from selenium.webdriver.common.by import By
def proxy_login_extension(folder, login, password):
"""Writes a tiny Chrome extension that answers the proxy's login prompt."""
d = pathlib.Path(folder)
d.mkdir(exist_ok=True)
(d / "manifest.json").write_text(json.dumps({
"manifest_version": 3, "name": "proxy-login", "version": "1.0",
"permissions": ["webRequest", "webRequestAuthProvider"],
"host_permissions": ["<all_urls>"],
"background": {"service_worker": "sw.js"},
}))
creds = json.dumps({"username": login, "password": password})
(d / "sw.js").write_text(
"chrome.webRequest.onAuthRequired.addListener("
f"(d, done) => done(d.isProxy ? {{authCredentials: {creds}}} : {{}}),"
"{urls: ['<all_urls>']}, ['asyncBlocking']);"
)
return str(d.resolve())
ext = proxy_login_extension("proxy-login", "LOGIN-cc-us-sid-ab12cd34-ttl-30", "PASSWORD")
options = webdriver.ChromeOptions()
options.add_argument("--proxy-server=http://gate.aeonproxy.com:7000")
options.enable_bidi = True
options.enable_webextensions = True # lets Selenium install an unpacked extension
driver = webdriver.Chrome(options=options)
driver.webextension.install(path=ext)
driver.get("https://api.ipify.org")
print(driver.find_element(By.TAG_NAME, "body").text)
driver.quit()enable_webextensions adds Chrome's --enable-unsafe-extension-debugging and --remote-debugging-pipe flags. Selenium warns that the pipe turns off many DevTools (CDP) features, so keep CDP calls out of this setup.
SOCKS5
Chrome supports no login for SOCKS5 proxies, and neither option changes that: with --proxy-server=socks5://gate.aeonproxy.com:7001 pages fail with ERR_SOCKS_CONNECTION_FAILED. Use the HTTP port 7000.
Rotating or sticky
A browser opens many connections for one page, and a rotating login gives each a new IP, so one page can load from several addresses. Use a sticky login per driver, such as LOGIN-cc-us-sid-ab12cd34-ttl-30, and start a new driver with a new key when you need a new IP. To check the exit, open IP checker in the driver: it shows the IP, location and WebRTC leaks.
If it does not work
| What you see | What to check |
|---|---|
Timed out waiting for response to BiDi command, the first page never loads | The first page was opened with driver.get(). Open it with browsing_context.navigate, as in option 1. |
ERR_INVALID_AUTH_CREDENTIALS | No handler answered, or the login, the password or a key in the login is wrong. |
ERR_NO_SUPPORTED_PROXIES | A login inside --proxy-server. Keep only http://gate.aeonproxy.com:7000 there. |
net::ERR_TUNNEL_CONNECTION_FAILED | The gateway refused the tunnel, and Chrome does not show the code: usually 402 (no traffic left), 403 (a target blocked by default) or 502 (the exit failed, retry). curl with the same login prints it: CONNECT tunnel failed, response 402. |
ERR_SOCKS_CONNECTION_FAILED | SOCKS5 with a login, which Chrome cannot do. Use port 7000. |
| The extension does nothing | Branded Chrome 137 or newer ignores --load-extension. Install it with driver.webextension.install() as above. |
Questions
Yes. We ran both with --headless=new.
Yes. Each driver has its own login, so LOGIN-cc-de-sid-de01 in one and LOGIN-cc-jp-sid-jp01 in another run side by side. Bought traffic has no thread limit.
Its GitHub repository is archived and its last release is from 2022, so this guide does not use it. Both options above work with Selenium alone.