What SeleniumBase UC Mode Actually Changes in Chrome
SeleniumBase UC Mode is a stealth driver mode built on undetected-chromedriver. It launches your installed Chrome before ChromeDriver attaches, then disconnects the driver while pages load, buttons get clicked and forms submit. You switch it on with uc=True (or --uc) and drive the page with uc_* helpers instead of plain driver.get(). This tutorial covers installation, driver-versus-manager syntax, proxy authentication, CAPTCHA handling, common errors and a full working script.
📌 TL;DR Executive Summary
- Core Takeaway: UC Mode is one flag (
uc=True) plus a set ofuc_*methods. Chrome starts first, ChromeDriver attaches second, and the driver leaves the wire during navigation and clicks. - Key Risk/Challenge: Headless runs are detectable, each launch costs several extra seconds, and authenticated proxies can silently stop receiving
Proxy-Authorizationheaders whileuc=Trueis active. - Recommended Solution: Run headed or through the SB manager on Linux, reuse one browser across a whole URL queue instead of relaunching per job, and keep CDP Mode as the fallback for both detection and proxy-auth failures.
Prerequisites Before You Run Anything
UC Mode needs less set-up than most people expect, because SeleniumBase handles the driver side for you. Four things have to be in place first.
- A current Python 3 install. Use a virtual environment so SeleniumBase’s dependency tree stays isolated from your other projects.
- SeleniumBase installed. One command:
pip install seleniumbase. UC Mode ships inside the package — there is no separate UC add-on and no manual driver download. - A real Chrome installed. Google Chrome is the default browser UC Mode drives. The
--chromium,--cft,--edgeand--braveflags switch to unbranded Chromium, Chrome-for-Testing, Edge or Brave instead — though only Chromium and Chrome-for-Testing download automatically. Version matching is left to SeleniumBase, which reads your Chrome build and fetches the right driver, so you never passversion_mainby hand. - A display on servers. A headless Linux box has no screen, and UC Mode’s CAPTCHA clicks need one; the SB manager supplies a virtual display.
Confirm the install resolved cleanly before writing a script:
# In your shell, not inside Python:
# pip install seleniumbase
# python -c "import seleniumbase; print(seleniumbase.__version__)"
If you want to see the framework’s own working code before you trust it, the SeleniumBase repository ships over 150 ready-to-run examples in its examples/ folder. Files named test_*.py or *_test.py run under pytest, which is a fast way to sanity-check your environment.
Step 1 — Launch UC Mode with the Driver Format
The Driver format is the smallest entry point and the closest to plain Selenium, so existing scripts port over with two changes: the import and the uc=True argument.
from seleniumbase import Driver
driver = Driver(uc=True)
try:
# With UC Mode, driver.get() is modified: it may silently route through
# uc_open_with_reconnect(url), which detaches ChromeDriver during the load.
driver.get("https://example.com")
print("title:", driver.title)
print("webdriver flag:", driver.execute_script("return navigator.webdriver"))
# When you want a plain, unmodified navigation instead:
driver.default_get("https://example.com/pricing")
finally:
driver.quit()
That driver.get() behaviour surprises people. In UC Mode it can call uc_open_with_reconnect() on your behalf, so the driver disconnects, the page loads unsupervised, and the connection returns a few seconds later. When you want the original behaviour — a health check, a JSON endpoint, any page without bot checks — call driver.default_get(url) instead.
What the mode changes underneath
UC Mode is built on undetected-chromedriver, so it inherits the browser-first launch: Chrome is up and past its startup checks before ChromeDriver attaches. It also renames the Chrome DevTools Console variables detection scripts look for, randomises the window.cdc_* properties a patched ChromeDriver leaks, and issues navigation or clicks through JavaScript while the driver is detached — so the page’s inspection code sees a complete browser with no automation client on the wire.
In a published benchmark, that was enough to flip navigator.webdriver from true to false and change the user agent from HeadlessChrome/151 to Chrome/151. Four other signals checked in the same session were unchanged: the same navigator.plugins length and the same reported platform in both runs. Read that as “UC Mode fixes the loud signals”, not as an invisible fingerprint.
driver.quit() in a bare script that can throw. Wrap the body in try/finally, or move to the SB manager in the next step, which closes the browser when the with block ends. Leftover Chrome processes hold their profile locks and break the next launch.
Step 2 — Move to the SB Manager for Real Work
The Driver format is a thin wrapper. The SB manager is what SeleniumBase builds its CAPTCHA handling around, because it adds a virtual display on headless Linux — the piece that makes operating-system-level clicks possible on a machine with no monitor — and closes the browser for you.
from seleniumbase import SB
with SB(uc=True, incognito=True) as sb:
# Explicit, controlled reconnect window:
sb.uc_open_with_reconnect("https://example.com/login", 4)
sb.type("input#email", "you@example.com")
sb.type("input#password", "your-password")
sb.uc_click("button[type=submit]")
sb.sleep(2)
print(sb.get_title())
Keep incognito=True paired with uc=True. Some sites detect a normal Chrome profile after a UC Mode launch, so incognito is sometimes required to maximise anti-detection. Match your browser language to the proxy’s country while you are at it: a US exit IP with a mismatched Accept-Language header is an easy pattern for a page to flag.
Which format should you use?
| Driver format | SB manager format | |
|---|---|---|
| Import | from seleniumbase import Driver |
from seleniumbase import SB |
| Launch | driver = Driver(uc=True) |
with SB(uc=True) as sb: |
| Cleanup | You call driver.quit() |
Automatic at the end of the block |
| Virtual display for GUI clicks | Not included | Included |
| Best for | Porting existing Selenium code | CAPTCHA clicks, pytest suites, Linux servers |
There is a second cost to weigh, and it is measured in seconds. One benchmark clocked a launch at 1.4 seconds with uc=False and 10.4 seconds with uc=True on the same machine — about nine extra seconds before your first request. The disconnect/reconnect cycle makes UC Mode roughly two to five times slower than plain Selenium overall.
That changes how you design a job. Launching a browser per URL means paying nine seconds for a 300-millisecond page; launching once and walking a queue of 50 URLs amortises it to under a fifth of a second each. Decide up front whether each job needs a fresh exit IP or a stable one — the trade-offs are covered in sticky versus rotating sessions — then group URLs so each browser launch covers one session shape.
Step 3 — Handle CAPTCHAs with the uc_gui_* Methods
UC Mode ships methods that click CAPTCHA widgets at the operating-system level rather than through the DOM. That matters, because a synthetic DOM click on a Turnstile checkbox is one of the easiest things for the widget to reject.
from seleniumbase import SB
with SB(uc=True, incognito=True) as sb:
sb.uc_open_with_reconnect("https://example.com/signup", 4)
# Click a CAPTCHA checkbox with a real OS-level mouse event.
sb.uc_gui_click_captcha()
sb.sleep(2)
# Type through the OS instead of the DOM when a field is guarded.
sb.uc_gui_write("input#email", "you@example.com")
sb.uc_gui_press_key("TAB")
# Fallback for widgets that need a full solve cycle rather than one click.
sb.uc_gui_handle_captcha()
Both uc_gui_click_captcha() and uc_gui_handle_captcha() auto-detect whether the challenge is a Cloudflare Turnstile or a Google reCAPTCHA, so you rarely inspect the iframe yourself. On Linux, prefer uc_gui_click_captcha() — the documentation calls it the more stealthy of the two. The related helpers are uc_gui_write() for typing and uc_gui_press_key() for key events such as Tab, useful when a form refuses DOM-level input.
Because these methods drive the real cursor and keyboard, they need a display. On a headless Linux box the SB manager starts a virtual display so PyAutoGUI actions still land, which is exactly why the docs tell you to use the manager rather than the Driver format on servers. If you also keep many logged-in profiles on that machine, isolation is a separate problem — see how antidetect browsers for Linux separate cookie jars and fingerprints, which UC Mode deliberately does not do for you.
sb.is_element_visible("iframe[title*='reCAPTCHA']") or the Turnstile equivalent — and only click when it is on screen. Clicking at coordinates where nothing exists wastes seconds you already paid for at launch.
Step 4 — Navigate Without Losing the Stealth Window
The stealth window is the period when ChromeDriver is detached. Re-attach too early — read the DOM, check an element, call a plain get() — and the page can observe an automation client before its checks finish. The UC Mode method set exists to keep you inside that window.
uc_open(url)— open a page through the UC path.uc_open_with_reconnect(url, seconds)— open, waitsecondsdetached, then reconnect. This is the workhorse call.uc_click(selector)— click through the UC path, detaching the driver around the click so the resulting navigation runs unsupervised.disconnect()andreconnect(timeout)— manual control when a multi-step sequence must finish before anyone reads the page.
Automate Seleniumbase Uc Mode With Send.win
Send.win pairs isolated, fingerprint-managed browser profiles with a full Automation API, so your scripts run in profiles that look and behave like real, separate users:
- Selenium, Puppeteer & Playwright support – drive any profile programmatically (Team plan)
- Isolated profiles – each with its own fingerprint, cookies, and storage
- Built-in residential proxies – with automatic timezone, locale, and WebRTC matching
- Desktop app for Windows, macOS & Linux – plus cloud sessions when you don’t want a local install
Try the instant cloud browser demo — no install, straight from your browser. Then compare plans: a 30-day free trial with no credit card, and paid plans from $6.99/month billed annually.
from seleniumbase import Driver
driver = Driver(uc=True, incognito=True)
try:
driver.uc_open_with_reconnect("https://example.com/checkout", 3)
# A flow that runs its own checks after the first paint:
driver.disconnect()
# Nothing is attached to this browser right now. The page runs its
# verification scripts, timers and network calls undisturbed.
driver.reconnect(5)
driver.uc_click("button#confirm")
print("final url:", driver.current_url)
finally:
driver.quit()
The rule of thumb: let the page finish its own timeline before you touch it. Three to five seconds of reconnect time costs far less than a blocked session, and it is the setting most people shorten too aggressively. When your targets span unrelated platforms the identity questions multiply — how to choose an antidetect browser covers when a scraping stack is the wrong tool for the job.
Step 5 — Add a Proxy with Username and Password
SeleniumBase accepts a proxy in three interchangeable places, all routed through the same launch code: the proxy= argument to Driver(), the proxy= argument to SB(), or the --proxy pytest flag. For Chrome and Edge, authenticated HTTP/HTTPS proxies work because SeleniumBase generates a small Manifest V3 extension on the fly, calling chrome.proxy.settings.set() and registering a chrome.webRequest.onAuthRequired listener that answers 407 challenges automatically. That is what prevents the native login popup.
from urllib.parse import quote
from seleniumbase import Driver
PROXY_HOST = "gate.example.net"
PROXY_PORT = "8000"
PROXY_USER = "your-user"
PROXY_PASS = "p@ss:w0rd" # note the colon — this is the case that breaks naive f-strings
# Percent-encode credentials before pasting them into the URL.
# An unescaped ':' or '@' in the password turns the host into part of the password.
user = quote(PROXY_USER, safe="")
password = quote(PROXY_PASS, safe="")
proxy = f"http://{user}:{password}@{PROXY_HOST}:{PROXY_PORT}"
driver = Driver(uc=True, proxy=proxy)
try:
driver.get("https://httpbin.org/ip")
print(driver.find_element("tag name", "body").text)
finally:
driver.quit()
That quoting step is not optional. If your password contains a colon or an at sign, an unescaped string produces a URL the parser reads incorrectly, and you get a connection error that looks like a dead proxy. Percent-encoding both fields costs two lines and removes an entire class of confusing failures. If you are still choosing a provider, it pays to compare residential proxy prices per gigabyte, because that number — not the headline — is what scales with your queue.
uc=True on Chrome 151, one benchmark recorded sixteen requests reaching an authenticated proxy with no Proxy-Authorization header at all, across three consecutive runs — while the same script authenticated correctly with UC Mode off. The suspected cause is a Chrome 137 change to extension internals rather than a SeleniumBase bug. If your traffic exits through the wrong path, whitelist your server IP for that session, update SeleniumBase for the extension fix, or activate CDP Mode before the first navigation.
Step 6 — When UC Mode Is Not Enough: CDP Mode
SeleniumBase’s documentation now points at CDP Mode as the successor to plain UC Mode for maximum stealth with Chromium browsers. Enter it from a UC session with sb.activate_cdp_mode(), or run it standalone as Pure CDP Mode via sb_cdp.Chrome(). Inside, sb.solve_captcha() handles challenges that were not bypassed automatically, and does nothing at all when no CAPTCHA is present.
from seleniumbase import SB
with SB(uc=True) as sb:
sb.activate_cdp_mode()
sb.goto("https://example.com/login")
sb.type("#email", "you@example.com")
sb.click("button[type=submit]")
sb.sleep(3)
sb.solve_captcha()
sb.sleep(2)
Stealthy Playwright Mode
SeleniumBase also exposes a stealthy Playwright mode: Playwright connects to a SeleniumBase browser over CDP instead of launching its own bundled Chromium. That is useful when your team already writes Playwright and does not want to maintain two stacks.
from playwright.sync_api import sync_playwright
from seleniumbase import sb_cdp
sb = sb_cdp.Chrome()
endpoint_url = sb.get_endpoint_url()
with sync_playwright() as p:
browser = p.chromium.connect_over_cdp(endpoint_url)
page = browser.contexts[0].pages[0]
page.goto("https://example.com")
print(page.title())
| UC Mode | CDP Mode | |
|---|---|---|
| Enabled by | uc=True / --uc |
sb.activate_cdp_mode() or sb_cdp.Chrome() |
| Driver during page load | Disconnects and reconnects | Drives the browser over the DevTools protocol |
| CAPTCHA call | uc_gui_click_captcha(), uc_gui_handle_captcha() |
solve_captcha() |
| Playwright support | No | Yes, via connect_over_cdp(endpoint_url) |
| Positioning | The classic stealth mode | Presented as the successor for maximum stealth |
The same CDP hook is how you attach automation to a managed profile instead of a throwaway browser — for example a Sendwin Browser profile opened through the Team-plan Automation API, where you copy the endpoint from the profile’s own settings rather than guessing it:
from playwright.sync_api import sync_playwright
CDP_URL = "http://127.0.0.1:PORT" # copy it from the profile's automation settings
with sync_playwright() as p:
browser = p.chromium.connect_over_cdp(CDP_URL)
page = browser.contexts[0].pages[0]
page.goto("https://example.com")
Reach for that when the session has to keep its identity. The profile already carries its own cookies, login state and fingerprint, so a session warmed over weeks keeps working. UC Mode is built for clean, repeatable requests; it is not a profile manager.
Common Errors and How to Fix Them
| Symptom | Likely cause | Fix |
|---|---|---|
| Endless challenge loop on a server | Running headless | Run headed, or use the SB manager (or xvfb=True) so a virtual display exists |
navigator.webdriver still true |
Plain Selenium, or a launch that skipped uc=True |
Confirm the flag, then read the value back with execute_script |
| Proxy login popup or 407 | The auth extension did not attach after a Chrome update | Update SeleniumBase, pass credentials through the proxy= argument, or switch into CDP Mode before navigating |
uc_gui_click_captcha() does nothing |
Headless Linux with the Driver format, which has no display | Move to the SB manager so the virtual display starts |
| Launch takes about ten seconds | Expected UC Mode overhead | Reuse one browser for a queue of URLs instead of relaunching |
| ChromeDriver version mismatch | Chrome updated faster than your driver | Let SeleniumBase fetch and match the driver; in the same benchmark, undetected-chromedriver 3.5.5 only connected once given version_main=151 |
The proxy works without uc=True and fails with it
Isolate it in three steps. Curl the proxy from the same machine to prove the credentials are good. Update SeleniumBase. Then run the identical script with uc=True removed — if the page loads without UC Mode, you have reproduced the known authentication gap rather than a bad proxy. At that point choose a workaround: whitelist the server’s IP so the provider authenticates by source address, or activate CDP Mode before the first navigation.
CAPTCHA clicks do nothing on a headless server
Operating-system-level clicks need a screen, and the Driver format does not provide one while the SB manager does. If you must stay with Driver(), pass xvfb=True explicitly, or install Xvfb and set the display yourself before the process starts.
SOCKS5 is not HTTP
SOCKS5 strings are syntactically valid in the same proxy= argument, and public guides rarely test them with UC Mode. The automatic authentication extension, however, is documented for authenticated HTTP/HTTPS proxies on Chrome and Edge, with no documented equivalent for SOCKS5 credentials. Test a SOCKS5 exit against a known IP-echo page before you build a pipeline on it.
The Full Script: One Browser, Many URLs
This is the shape that fits most real jobs: one browser, a queue of targets, credential-safe proxy handling, a CAPTCHA check that only fires when a widget exists, per-URL error isolation, and a screenshot for debugging.
# uc_queue.py — one UC Mode browser, many URLs, proxy auth, CAPTCHA fallback.
import logging
import os
from urllib.parse import quote
from seleniumbase import SB
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
log = logging.getLogger("uc")
PROXY_HOST = os.environ.get("PROXY_HOST", "gate.example.net")
PROXY_PORT = os.environ.get("PROXY_PORT", "8000")
PROXY_USER = os.environ.get("PROXY_USER", "proxy-user")
PROXY_PASS = os.environ.get("PROXY_PASS", "p@ss:word") # keep secrets out of the file
TARGETS = [
"https://example.com/login",
"https://example.com/pricing",
"https://example.com/docs",
]
RECONNECT_SECONDS = 4
CAPTCHA_SELECTOR = "iframe[src*='challenges.cloudflare.com'], iframe[title*='reCAPTCHA']"
def proxy_string():
"""Percent-encode credentials so ':' and '@' inside them cannot break the URL."""
user = quote(PROXY_USER, safe="")
password = quote(PROXY_PASS, safe="")
return f"http://{user}:{password}@{PROXY_HOST}:{PROXY_PORT}"
def main():
with SB(uc=True, incognito=True, proxy=proxy_string()) as sb:
for url in TARGETS:
try:
sb.uc_open_with_reconnect(url, RECONNECT_SECONDS)
sb.sleep(1)
# Only click when a challenge widget is actually rendered.
if sb.is_element_visible(CAPTCHA_SELECTOR):
log.info("challenge detected on %s", url)
sb.uc_gui_click_captcha()
sb.sleep(2)
log.info("OK %s -> %s", url, sb.get_title())
except Exception as exc:
log.warning("FAIL %s -> %s", url, exc)
continue
sb.save_screenshot("last_page.png")
if __name__ == "__main__":
main()
Two details are worth keeping when you adapt it. The proxy string is built once, outside the loop, so a typo fails immediately rather than on the third URL. And each target is wrapped in its own try block, because a single timeout should not cost you the remaining queue — the browser is already paid for.
🏆 Send.win Verdict
UC Mode solves the client-side half of the problem: it hides the automation client during page loads and gives you operating-system-level CAPTCHA clicks. It is not a profile manager. When the job is keeping twenty logged-in marketplace or ad accounts alive, each with its own cookies and fingerprint, relaunching a stealth browser per session does not help — every account still shares one machine identity. Send.win covers that side: isolated profiles with coherent fingerprints, built-in residential proxies whose timezone and locale follow the exit IP automatically, cloud sync so logins follow you across devices, and profile sharing that opens a teammate’s session already signed in. On the Team plan the local Automation API covers Selenium, Puppeteer and Playwright, so the scripts above and your managed profiles can live in one workflow.
Try Send.win free today — 30 days at $0, cancel anytime, with 10 isolated profiles and free residential proxies to point your scripts at.
Frequently Asked Questions
What is SeleniumBase UC Mode and how does it work?
It is a stealth driver mode built on undetected-chromedriver, based on that library and extended with uc_* methods. Chrome launches first and ChromeDriver attaches afterwards, the driver is disconnected during page loads, clicks and form submissions, and Console variables plus the window.cdc_* properties are renamed so detection scripts do not find a standard automation signature.
Is SeleniumBase UC Mode detectable when running headless?
Yes, and the framework’s own guidance says running headless with UC Mode makes it detectable. That is why a virtual display is the recommended route on servers — xvfb=True or the SB manager. In one benchmark the user agent read HeadlessChrome/151 with UC Mode off and Chrome/151 with it on.
Why does my proxy stop authenticating when uc=True?
There is a known failure where requests reach an authenticated proxy without a Proxy-Authorization header while UC Mode is active — one benchmark recorded sixteen such requests across three runs, while the same script authenticated fine with UC Mode off. The suspected cause is a Chrome 137 change to extension internals, not a SeleniumBase bug. Update SeleniumBase, whitelist your server IP, or activate CDP Mode before navigating.
What is the difference between uc_gui_click_captcha and uc_gui_handle_captcha?
Both auto-detect whether the widget is a Cloudflare Turnstile or a Google reCAPTCHA. uc_gui_click_captcha() performs the click, while uc_gui_handle_captcha() runs a fuller handle cycle. On Linux, SeleniumBase recommends the click version because it is the more stealthy of the two.
Should I use the Driver format or the SB manager format?
Use Driver() when you are porting existing Selenium code and want the smallest change. Use the SB() context manager for anything involving CAPTCHA clicks, pytest runs or headless Linux — it handles browser cleanup and includes the virtual display that makes operating-system-level clicks possible.
How do I add a proxy with username and password in SeleniumBase?
Pass a single string to the proxy= argument of Driver() or SB(), or use the --proxy pytest flag; all three route through the same launch code. Percent-encode the username and password with urllib.parse.quote first, because a colon or at sign inside a credential breaks the URL silently.
Is CDP Mode better than UC Mode for stealth?
The project now points to CDP Mode as the successor to plain UC Mode and positions it as maximum stealth for Chromium browsers. It also unlocks connect_over_cdp() for Playwright and sb.solve_captcha() for challenges that were not bypassed automatically. In practice, start with UC Mode and move to CDP Mode when a target keeps challenging you or when proxy authentication misbehaves.
Does SeleniumBase UC Mode work with SOCKS5 proxies?
SOCKS5 strings are accepted by the same proxy= argument, but the automatic authentication extension is documented for HTTP/HTTPS proxies on Chrome and Edge, with no documented equivalent for SOCKS5 credentials. Test a SOCKS5 exit against a known IP-echo page before building a pipeline on it, and keep authenticated HTTP/HTTPS as the supported path.