---
title: "How to Bypass CAPTCHA With Selenium: Best Methods"
description: "Understand how to handle CAPTCHA in Selenium using stealth browser setup, cleaner proxies, realistic behavior, and CAPTCHA solver workflows."
url: "https://marsproxies.com/blog/bypass-captcha-selenium/"
---

- [EN ](https://marsproxies.com/blog/bypass-captcha-selenium/ "English")
  [CN](https://marsproxies.cn/blog/bypass-captcha-selenium/ "Chinese")

[Contact sales](https://marsproxies.com/contact-sales/) [Register](https://dashboard.marsproxies.com/register/) [Log in](https://dashboard.marsproxies.com/login/)

[Contact sales](https://marsproxies.com/contact-sales/) [Register](https://dashboard.marsproxies.com/register/) [Log in](https://dashboard.marsproxies.com/login/)

[Contact sales](https://marsproxies.com/contact-sales/)

[Log in](https://dashboard.marsproxies.com/login/) [Register](https://dashboard.marsproxies.com/register/)

- [EN ](https://marsproxies.com/blog/bypass-captcha-selenium/ "English")
  [CN](https://marsproxies.cn/blog/bypass-captcha-selenium/ "Chinese")

[Back to blog](https://marsproxies.com/blog/) June 19, 2026

# How to Bypass CAPTCHA With Selenium: Best Methods

[How to](https://marsproxies.com/blog/category/how-to/)

\-

Table of contents

1. [Why Selenium triggers CAPTCHA](https://marsproxies.com/blog/bypass-captcha-selenium/#b-why-selenium-triggers-captcha)
2. [Method 1: Use a stealthier Selenium browser setup](https://marsproxies.com/blog/bypass-captcha-selenium/#b-method-1-use-a-stealthier-selenium-browser-setup)
3. [undetected-chromedriver](https://marsproxies.com/blog/bypass-captcha-selenium/#b-undetected-chromedriver)
4. [Remove obvious automation signals](https://marsproxies.com/blog/bypass-captcha-selenium/#b-remove-obvious-automation-signals)
5. [Method 2: Improve IP reputation with proxies](https://marsproxies.com/blog/bypass-captcha-selenium/#b-method-2-improve-ip-reputation-with-proxies)
6. [Residential vs datacenter vs mobile proxies](https://marsproxies.com/blog/bypass-captcha-selenium/#b-residential-vs-datacenter-vs-mobile-proxies)
7. [Rotating vs sticky sessions](https://marsproxies.com/blog/bypass-captcha-selenium/#b-rotating-vs-sticky-sessions)
8. [Method 3: Improve session and behavior management](https://marsproxies.com/blog/bypass-captcha-selenium/#b-method-3-improve-session-and-behavior-management)
9. [Reuse those cookies and sessions](https://marsproxies.com/blog/bypass-captcha-selenium/#b-reuse-those-cookies-and-sessions)
10. [Avoid repeated cold starts](https://marsproxies.com/blog/bypass-captcha-selenium/#b-avoid-repeated-cold-starts)
11. [Use realistic waits and navigation patterns](https://marsproxies.com/blog/bypass-captcha-selenium/#b-use-realistic-waits-and-navigation-patterns)
12. [Lower request volume and retry spikes](https://marsproxies.com/blog/bypass-captcha-selenium/#b-lower-request-volume-and-retry-spikes)
13. [Method 4: Use a CAPTCHA solver service](https://marsproxies.com/blog/bypass-captcha-selenium/#b-method-4-use-a-captcha-solver-service)
14. [Token-based vs interactive solving](https://marsproxies.com/blog/bypass-captcha-selenium/#b-token-based-vs-interactive-solving)
15. [Supported challenge types](https://marsproxies.com/blog/bypass-captcha-selenium/#b-supported-challenge-types)
16. [The trade-offs of using CAPTCHA solvers](https://marsproxies.com/blog/bypass-captcha-selenium/#b-the-trade-offs-of-using-captcha-solvers)
17. [A step-by-step workflow for CAPTCHA in Selenium](https://marsproxies.com/blog/bypass-captcha-selenium/#b-a-step-by-step-workflow-for-captcha-in-selenium)
18. [Getting started with a stealth browser setup](https://marsproxies.com/blog/bypass-captcha-selenium/#b-getting-started-with-a-stealth-browser-setup)
19. [Add behavior tuning and session persistence](https://marsproxies.com/blog/bypass-captcha-selenium/#b-add-behavior-tuning-and-session-persistence)
20. [Add proxies if CAPTCHA still appears](https://marsproxies.com/blog/bypass-captcha-selenium/#b-add-proxies-if-captcha-still-appears)
21. [Add a solver only when needed](https://marsproxies.com/blog/bypass-captcha-selenium/#b-add-a-solver-only-when-needed)
22. [Measure CAPTCHA rates and failures](https://marsproxies.com/blog/bypass-captcha-selenium/#b-measure-captcha-rates-and-failures)
23. [Selenium CAPTCHA troubleshooting](https://marsproxies.com/blog/bypass-captcha-selenium/#b-selenium-captcha-troubleshooting)
24. [Conclusion](https://marsproxies.com/blog/bypass-captcha-selenium/#b-conclusion)
25. [FAQ](https://marsproxies.com/blog/bypass-captcha-selenium/#b-faq)

\-

**Key takeaways**

- CAPTCHA in Selenium is usually a symptom of bot detection.
- Start with prevention before solving.
- Use proxies as part of a full identity profile, not as a standalone fix.

CAPTCHA blocks should never feel like a strange exception when running Selenium Python scripts. They usually indicate that the site has noticed signals that make your Selenium session look less like typical human browsing.

That's why you should understand CAPTCHA bypass techniques before a site's bot detection mechanism breaks your workflow. This article walks through the best methods to bypass CAPTCHA Selenium issues, including Selenium Stealth, clean proxy use, and CAPTCHA solver workflows.

## Why Selenium triggers CAPTCHA

First, let’s look at why the website you are trying to scrape, or test, showed that annoying “prove you are human” box. Selenium can trigger CAPTCHA blocks when your browser setup, IP address, or browsing behavior starts to look automated.

Here are some of the possible reasons:

- **Browser fingerprinting and WebDriver signals**

A browser fingerprint is the set of small details your browser reveals when interacting with a website. CAPTCHA becomes more likely when a session reports itself as Chrome on Windows, but other signals suggest automation, virtualization, or an inconsistent device setup.

- [**Headless browser**](https://marsproxies.com/blog/headless-browser/)**&#x20;detection**

Selenium can run Chrome in headless mode or headful mode. Headless mode is useful because the browser runs in the background. However, it can leave signals that websites recognize, such as odd window dimensions, virtual graphics rendering, or limited signs of normal user activity.

- **Unnatural behavior patterns**

Real users don’t browse in perfectly even steps. They pause, scroll unevenly, read content, hesitate before clicking, and make small movements. A basic Selenium script opens a page, waits one second, clicks, types instantly, submits, and repeats this pattern across pages, making the session look automated.

- **Weak or overused IPs**

Even if your browser setup looks clean, a bad IP address can trigger CAPTCHA before the site looks at your behavior. Free or overused proxies may already be tied to spam or repeated CAPTCHA challenges, so the session starts with a higher risk score.

- **Missing session continuity**

Normal browsers store history in cookies, cache, local storage, and previous page paths. Basic Selenium setups start with a new browser profile on every visit, which can make each visit feel disposable. When that clean session also moves quickly or lands directly on important pages, CAPTCHA becomes more likely.

The best [CAPTCHA bypass approach](https://marsproxies.com/blog/bypass-captcha/) is layered. Start by making Selenium harder to detect, then improve proxy quality and behavior patterns, and only use a solver when a challenge still appears.

## Method 1: Use a stealthier Selenium browser setup

Now that you know why Selenium triggers CAPTCHA, the next step is to reduce the signals that make your browser look automated in the first place.

### undetected-chromedriver

A standard Selenium exposes signals such as:

- navigator.webdriver === true
- Missing or incomplete Chrome runtime objects
- Automation flags in the DevTools Protocol

Instead of standard Selenium, install undetected-chromedriver. It patches parts of Chrome before the browser starts, so the session does not carry some of the most obvious Selenium fingerprints.

undetected-chromedriver can help by:

- Removing the "cdc\_" string from ChromeDriver, which is a known fingerprint
- Overwriting navigator.webdriver behavior so it returns undefined
- Adding missing Chrome objects, such as window\.chrome and runtime properties
- Disabling the “Chrome is being controlled by automated test software” infobar

The result is a browser that looks like a normal Chrome instance rather than one controlled by Selenium WebDriver.

### Remove obvious automation signals

Tools like Selenium Stealth can help you remove obvious automation signals. Selenium Stealth is a Python package that reduces the likelihood of Chrome/Chromium being detected as a bot when controlled by Selenium. It does this by modifying browser fingerprint properties to prevent automation leaks.

The most common signals include:

- navigator.webdriver === true
- Missing [window.chrome](http://window.chrome/) object in Chrome or Edge
- Incomplete plugins and languages
- Missing or abnormal navigator.connection
- Chrome DevTools Protocol flags
- The cdc\_ string in ChromeDriver
- User agent and platform mismatches

## Method 2: Improve IP reputation with proxies

Once you’ve set up a stealthier Selenium browser, the next step is to pair it with reputable proxies. IP quality matters because websites can log addresses that have been used for scraping, spam, failed login attempts, or repeated CAPTCHA challenges.

If your Selenium session comes from an IP address that has already been flagged, you may keep hitting CAPTCHAs even after cleaning up your browser settings.

### Residential vs datacenter vs mobile proxies

You will usually come across three options when choosing proxies for Selenium: residential, datacenter, and mobile proxies.

Residential proxies are particularly effective for avoiding CAPTCHAs because they route requests through real user devices. This makes them appear more legitimate to your target websites.

Use [residential proxies](https://marsproxies.com/proxies/residential-proxies/) when:

- You are scraping websites with stricter bot detection
- You need traffic that looks closer to normal user browsing
- You are working with location-sensitive pages
- You want to reduce the risk of repeated Google reCAPTCHA challenges
- You need a better balance between trust and scalability

Datacenter proxies, while fast and affordable, are easier for websites to detect and block compared to residential proxies. Using them on vigilant websites can be counteractive, triggering more frequent CAPTCHA challenges.

Use [datacenter proxies](https://marsproxies.com/proxies/datacenter-proxies/) when:

- You are scraping less-protected websites
- You need faster response times
- You are collecting public pages at a controlled pace
- You are testing Selenium code before using higher-trust proxies
- You don’t need the IP to look like residential traffic

[Mobile proxies](https://marsproxies.com/proxies/mobile-proxies/) use IPs assigned by mobile carriers, which makes them look highly trusted. Use them when:

- You are working with mobile-first websites
- You need carrier-based IPs for location testing
- Residential proxies are still being challenged too often
- The target site is sensitive to datacenter traffic

Stop Hitting CAPTCHAs in Your Selenium Scripts in 2026

[Get residential IPs](https://dashboard.marsproxies.com/products/ultra-residential-proxies#buy-now)

## Rotating vs sticky sessions

You can choose to rotate your proxies or use a single IP for a set duration, and that choice has a direct effect on whether your setup triggers a CAPTCHA. Using proxies to rotate IP addresses can help reduce the likelihood of triggering CAPTCHA challenges during web scraping, as it allows the scraper to appear as multiple organic users.

On top of that, your IP should be part of a larger behavior profile. When you rotate that IP, rotate everything else in that profile too. Otherwise, the session becomes inconsistent and easier to detect.

Here is what belongs in a complete profile:

- Time zone
- User agent
- Viewport size
- Hardware concurrency (navigator.hardwareConcurrency)
- Touch points (navigator.maxTouchPoints)
- Language and Accept-Language headers
- WebGL vendor string
- WebGL renderer string
- Device memory (navigator.deviceMemory)

Rotating your proxy while leaving all these fingerprints intact will almost certainly trigger a CAPTCHA. Rotation works best for repetitive tasks such as [Selenium web scraping](https://marsproxies.com/blog/selenium-web-scraping/), where spreading requests across multiple IPs can reduce pressure on a single address. Sticky sessions are better when logging into platforms or performing actions where a normal user would be expected to maintain a consistent identity.

## Method 3: Improve session and behavior management

The way your Selenium script operates can impact how often you hit a CAPTCHA. All those tiny adjustments in how you handle sessions and navigate through pages add up.

Here are some specific tricks to help your scraper blend in a bit more.

### Reuse those cookies and sessions

First, try reusing cookies and sessions across runs of your script. A browser that shows up with a familiar set of cookies looks a lot less like a brand-new disposable browser. Use these functions:

- save\_cookies(driver, filename) writes all cookies to a JSON file
- load\_cookies(driver, filename) reads that file back and adds the cookies to the browser before you navigate to the target domain

Your Selenium script should also use a persistent user data directory when it starts the browser via undetected-chromedriver. That way, you get to keep local storage, session storage, and all the other browser state intact from one run to the next.

### Avoid repeated cold starts

A cold start happens when you fire up a brand-new browser session without any prior activity on the target site. Websites notice this.

A browser that just appears out of the blue, with no stored cookies, cache, or history, can look like a brand-new scraper getting set up, which on its own can trigger CAPTCHA pop-ups. The better approach is to keep your browser instance open and alive across multiple requests. If your Selenium WebDriver session just keeps going, the site thinks you're a real visitor popping in again.

When you need to restart the browser, do a little warm-up session first, don't just head straight to the page you want to scrape. Spend a minute or two browsing some neutral site - a news homepage, a Wikipedia article, etc.

Have the browser scroll around a bit to let the analytics scripts run. Then navigate to the domain you actually want to scrape. That warm-up period helps fill in some of the missing signals that bot detection systems look for.

### Use realistic waits and navigation patterns

Writing time.sleep(2) has an automation giveaway written all over it. Your Selenium script pauses for exactly two seconds every single time. Let's be honest, no human does that.

We'd probably glance at our phone, maybe even click right away, because we already know where that button is. The thing is, humans are inconsistent, and your script should be too.

Here is what you should do:

- Swap out fixed waits for some random timing. A wait like random.uniform(1.5, 8.0) feels a lot more natural than a hard stop at 2 seconds.

* For elements that take a while to load, try using WebDriverWait with a flexible timeout. Add a small random jitter to the polling interval. Do not check every 0.1 seconds on the dot. Vary it.

- Also, your Selenium script shouldn't click every link or button in the same old order every single run. Be unpredictable - randomize the sequence. If you're on a search results page, try having your script type out the search term with a few typos, then correct them. Sometimes it's okay to click the wrong thing, hit the back button, and try again.

### Lower request volume and retry spikes

The single biggest factor in CAPTCHA avoidance is the number of requests you send over time. A low, steady rate is safer than bursts of activity.

If you need to scrape a large set of pages, spread the work across hours or days. Add random intervals between requests. For a site with strict bot detection systems, staying under 10 requests per minute per IP is a reasonable starting point.

Retry spikes are especially dangerous. When a request fails, many scripts retry immediately two or three times. That spike in traffic looks like an automated hammer.

Instead, implement exponential backoff. Wait five seconds after a first failure, 15 seconds after a second, then 45 seconds. If a proxy or session fails repeatedly, rotate to a fresh identity instead of using the same one.

## Method 4: Use a CAPTCHA solver service

So far, the methods we have covered are about playing defense. Their goal is to reduce the signals that make your Selenium WebDriver session look automated, so you can avoid CAPTCHAs before they interrupt your workflow.

But what happens when your script still triggers a CAPTCHA?

That is where a CAPTCHA solver service comes in. This method gives your script a way to handle CAPTCHA challenges in Selenium after they appear.

Here is how the process usually works:

- **Step 1:** Your script encounters a CAPTCHA

* **Step 2:** You send the challenge to the solver service

- **Step 3:** The service solves it using a human workforce or an automated solving system

* **Step 4:** Your script receives the solution

- **Step 5:** Your script submits the solution and continues the session

The important thing is to understand how these tools work before adding them to your Selenium setup.

### Token-based vs interactive solving

There are two kinds of CAPTCHA challenges that your Selenium Python script might run up against while web scraping: the token-based type and the interactive type.

- **Token-based solving** is typical with modern CAPTCHA systems like reCAPTCHA and hCaptcha. Here, the target website is more interested in whether the script has a valid token before deciding what to do next. Your script sends the CAPTCHA details to the API. The API then sends a token back to the script, which it can use to continue the session.

* **Interactive, click-based solving** is a different story. Some of the more advanced CAPTCHAs need the user to click on parts of an image, move a slider, or do another interactive task. When your script runs into this kind of challenge, it sends the challenge details to the API. The API then returns the coordinates or a list of actions needed to complete the job.

### Supported challenge types

The range of supported challenges can vary from one service provider to another, but most reputable CAPTCHA-solving services, like [CapSolver](https://marsproxies.com/blog/capsolver-captcha-solver/), cover the common CAPTCHA types users encounter during Selenium automation.

- **Google reCAPTCHA v2** includes the familiar “I’m not a robot” checkbox. After the user clicks the checkbox, the system may allow access immediately or trigger an additional image-grid challenge if it is uncertain about the user’s identity. These follow-up challenges ask users to identify objects such as traffic lights, buses, crosswalks, or bicycles.

* **Google reCAPTCHA v3** is an invisible, score-based system that works in the background without requiring direct user interaction. Instead of showing a checkbox or image challenge, it monitors behavioral signals and assigns a score that helps the website decide whether the visitor appears human or automated. For a deeper breakdown of how this scoring system works, see our guide on [how to bypass reCAPTCHA v3](https://marsproxies.com/blog/bypass-recaptcha-v3/).

- **hCaptcha** is a privacy-focused alternative to reCAPTCHA. It often presents image classification challenges and uses its own detection algorithms to evaluate whether the visitor is human. Many websites use hCaptcha as a replacement for Google’s reCAPTCHA system.

* **Image CAPTCHAs** are the old-school text-from-image challenges where users must type distorted letters, numbers, or words correctly. They are less common on modern websites but still appear on some forms, login pages, and older platforms.

- **Audio CAPTCHAs** provide an alternative for visually impaired users. Instead of solving a visual challenge, users listen to spoken characters, numbers, or words and type what they hear.

* **Cloudflare Turnstile** is a CAPTCHA alternative that relies on token validation and background checks rather than traditional puzzle-solving. In many cases, it verifies visitors with little or no visible user interaction.

## The trade-offs of using CAPTCHA solvers

| Aspect | Reality and trade-off |
| --- | --- |
| Cost | CAPTCHA solvers are usually inexpensive per solve, but the cost depends on the challenge type. Simple image CAPTCHAs are cheaper, while reCAPTCHA v2, reCAPTCHA Enterprise, FunCaptcha, and similar challenges usually cost more. |
| Latency | Solving takes time. Some challenges may be solved in a few seconds, while others can take 20 to 30 seconds or longer. This can slow down a Selenium workflow, especially at scale. |
| Reliability | Solver services are not perfect. Simple challenges tend to have higher success rates, while complex image grids or interactive challenges can fail more often. Timeouts and API errors can also happen. |
| Scalability | Most solver APIs can handle parallel requests, but your script still needs retry logic and fallback handling when a solve fails or takes too long |

## A step-by-step workflow for CAPTCHA in Selenium

Working around CAPTCHAs in Selenium requires a multi-layered approach, including architectural changes for testing and integrating specialized AI solving services for production.

Now, let's go through a practical workflow for creating a Selenium script that aims to avoid CAPTCHAs from day one.

### Getting started with a stealth browser setup

Here's a basic script that sets up a stealth browser using undetected\_chromedriver and some injected CDP scripts.

```python
import undetected_chromedriver as uc
```

```python
def launch_stealth_browser ( ) :
```

```javascript
    options = uc. ChromeOptions ( )
```

```javascript
    options. add_argument ( "--disable-blink-features=AutomationControlled" )
```

```javascript
    options. add_argument ( "--window-size=1366,768" )
```

```javascript
    options. add_argument ( "--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/121.0.0.0 Safari/537.36" )
```

```javascript
    driver = uc. Chrome (options=options)
```

```javascript
    # Inject stealth scripts before any page loads
```

```javascript
    stealth_script = """
```

```javascript
    Object. defineProperty (navigator, 'webdriver' , { get : ( ) => undefined } ) ;
```

```javascript
    window.chrome = { runtime : { } } ;
```

```javascript
    Object. defineProperty (navigator, 'plugins' , { get : ( ) => [ 1 , 2 , 3 , 4 , 5 ] } ) ;
```

```javascript
    Object. defineProperty (navigator, 'languages' , { get : ( ) => [ 'en-US' , 'en' ] } ) ;
```

```javascript
    """
```

```typescript
    driver. execute_cdp_cmd ( "Page.addScriptToEvaluateOnNewDocument" , { "source" : stealth_script} )
```

```javascript
    # Override timezone and geolocation
```

```javascript
    driver. execute_cdp_cmd ( "Emulation.setTimezoneOverride" , { "timezoneId" : "Europe/London" } )
```

```javascript
    driver. execute_cdp_cmd ( "Emulation.setGeolocationOverride" , { "latitude" : 51.5074 , "longitude" : - 0.1278 , "accuracy" : 100 } )
```

```javascript
    return driver
```

```javascript
# Usage
```

```javascript
driver = launch_stealth_browser ( )
```

```javascript
driver. get ( "https://bot.sannysoft.com" )
```

```javascript
input ( "Check the page for automation flags, then press Enter to quit." )
```

```javascript
driver. quit ( )
```

### Add behavior tuning and session persistence

A stealth browser alone is not enough. Your script also needs to act like a person. That means reusing cookies, typing with natural delays, and adding random pauses.

Here is a basic script that shows how these pieces fit together.

```python
import json
```

```python
import random
```

```python
import time
```

```python
from selenium.webdriver.common.by import By
```

```python
from selenium.webdriver.common.keys import Keys
```

```python
def save_cookies (driver, filename) :
```

```javascript
    with open (filename, "w" ) as f :
```

```javascript
        json. dump (driver. get_cookies ( ) , f)
```

```python
def load_cookies (driver, filename) :
```

```javascript
    driver. get ( "https://example.com" )  # Navigate to domain first
```

```javascript
    with open (filename, "r" ) as f :
```

```javascript
        cookies = json. load (f)
```

```javascript
    for cookie in cookies :
```

```javascript
        driver. add_cookie (cookie)
```

```python
def human_type (element, text) :
```

```javascript
    element. click ( )
```

```javascript
    # Clear field with random backspaces
```

```javascript
    for _ in range (random. randint ( 2 , 8 ) ) :
```

```javascript
        element. send_keys (Keys. BACKSPACE )
```

```javascript
        time. sleep (random. uniform ( 0.03 , 0.1 ) )
```

```javascript
    for char in text :
```

```javascript
        # 7 % chance of hesitation
```

```javascript
        if random. random ( ) < 0.07 :
```

```javascript
            time. sleep (random. uniform ( 0.2 , 0.6 ) )
```

```javascript
        element. send_keys (char)
```

```javascript
        time. sleep (random. uniform ( 0.08 , 0.3 ) )
```

```python
def random_wait (min_sec= 1 , max_sec= 5 ) :
```

```javascript
    time. sleep (random. uniform (min_sec, max_sec) )
```

```javascript
# Example usage
```

```javascript
driver = launch_stealth_browser ( )  # from previous example
```

```javascript
driver. get ( "https://google.com" )
```

```javascript
random_wait ( 2 , 4 )
```

```javascript
search_box = driver. find_element (By. NAME , "q" )
```

```javascript
human_type (search_box, "hello world" )
```

```javascript
random_wait ( 1 , 2 )
```

```javascript
search_box. send_keys (Keys. RETURN )
```

```javascript
# Save cookies after session
```

```javascript
save_cookies (driver, "my_cookies.json" )
```

```javascript
driver. quit ( )
```

```javascript
# Next run: load cookies
```

```javascript
driver = launch_stealth_browser ( )
```

```javascript
load_cookies (driver, "my_cookies.json" )
```

```javascript
driver. refresh ( )
```

### Add proxies if CAPTCHA still appears

Do not simply plug a proxy into your script and expect better results. Each IP address needs a matching identity profile. That means a unique user agent, viewport size, timezone, WebGL vendor, and language header. Here is a code snippet to show you how it is done:

```python
import undetected_chromedriver as uc
```

```python
import random
```

```python
import time
```

```python
from selenium.webdriver.common.by import By
```

```javascript
# Proxy pool (residential proxies with auth)
```

```javascript
PROXIES = [
```

```typescript
    "http://user1:pass1@proxy1.example.com:8080" ,
```

```typescript
    "http://user2:pass2@proxy2.example.com:8080" ,
```

```javascript
]
```

```javascript
# Simple identity profiles (just user agent and viewport for demo)
```

```javascript
IDENTITIES = [
```

```javascript
    { "user_agent" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/121" , "viewport" : ( 1366 , 768 ) } ,
```

```javascript
    { "user_agent" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) Safari/605" , "viewport" : ( 1440 , 900 ) } ,
```

```javascript
]
```

```python
def assign_proxy_and_identity ( ) :
```

```javascript
    proxy = random. choice ( PROXIES )
```

```javascript
    identity = random. choice ( IDENTITIES )
```

```javascript
    return proxy, identity
```

```python
def launch_browser_with_proxy (proxy, identity) :
```

```javascript
    options = uc. ChromeOptions ( )
```

```javascript
    options. add_argument (f"--proxy-server={proxy}" )
```

```javascript
    options. add_argument (f"--window-size={identity['viewport'][0]},{identity['viewport'][1]}" )
```

```javascript
    options. add_argument (f"--user-agent={identity['user_agent']}" )
```

```javascript
    options. add_argument ( "--disable-blink-features=AutomationControlled" )
```

```javascript
    return uc. Chrome (options=options)
```

```python
def captcha_detected (driver) :
```

```javascript
    # Basic detection – look for keywords in page text
```

```javascript
    body = driver. find_element (By. TAG_NAME , "body" ) .text. lower ( )
```

```javascript
    indicators = [ "captcha" , "verify you are human" , "recaptcha" ]
```

```javascript
    return any (ind in body for ind in indicators)
```

```javascript
# Main retry loop
```

```javascript
MAX_RETRIES = 3
```

```javascript
for attempt in range ( MAX_RETRIES ) :
```

```javascript
    proxy, identity = assign_proxy_and_identity ( )
```

```python
    print ( f"Attempt  {attempt+ 1 } : Proxy= {proxy.split( '@' ) [ 1 ] } , Identity= {identity[ 'user_agent' ] [ : 30] } ..." )
```

```javascript
    driver = launch_browser_with_proxy (proxy, identity)
```

```javascript
    driver. get ( "https://target-site-that-may-show-captcha.com" )
```

```javascript
    time. sleep ( 3 )
```

```javascript
    if captcha_detected (driver) :
```

```python
        print ( "CAPTCHA detected. Rotating proxy and identity..." )
```

```javascript
        driver. quit ( )
```

```javascript
        wait_time = (attempt + 1 ) * 10  # exponential backoff
```

```python
        print ( f"Waiting  {wait_time}  seconds before retry." )
```

```javascript
        time. sleep (wait_time)
```

```javascript
        continue  # next attempt with new proxy /identity
```

```javascript
    # No CAPTCHA – proceed with scraping
```

```python
    print ( "No CAPTCHA. Scraping successful." )
```

```javascript
    # ... do your work ...
```

```javascript
    driver. quit ( )
```

```javascript
    break
```

```javascript
else :
```

```python
    print ( "All attempts failed due to CAPTCHA." )
```

### Add a solver only when needed

Now, if you've implemented the undetected-chromedriver, behavioral changes, and added proxies, but your target is still asking your script to solve a CAPTCHA, you can fall back to the script below:

```python
import time
```

```python
import requests
```

```python
from selenium.webdriver.common.by import By
```

```python
from selenium.webdriver.support.ui import WebDriverWait
```

```python
from selenium.webdriver.support import expected_conditions as EC
```

```javascript
# === CONFIGURATION ===
```

```javascript
CAPTCHA_SOLVER_API_KEY = "YOUR_API_KEY_HERE"
```

```javascript
CAPTCHA_SOLVER_URL = "https://api.your-solver-service.com/in.php"  # Example endpoint
```

```javascript
CAPTCHA_SOLVER_RESULT_URL = "https://api.your-solver-service.com/res.php"
```

```javascript
# === CAPTCHA DETECTION ===
```

```python
def is_captcha_present (driver) :
```

```python
    """Return True if a CAPTCHA iframe or text is detected."""
```

```javascript
    try :
```

```javascript
        # Check for common CAPTCHA elements
```

```javascript
        if driver. find_elements (By. CSS_SELECTOR , "iframe[src*='recaptcha'], .g-recaptcha" ) :
```

```javascript
            return True
```

```javascript
        body_text = driver. find_element (By. TAG_NAME , "body" ) .text. lower ( )
```

```javascript
        indicators = [ "captcha" , "verify you are human" , "recaptcha" ]
```

```javascript
        return any (ind in body_text for ind in indicators)
```

```javascript
    except :
```

```javascript
        return False
```

```javascript
# === SOLVER INTEGRATION ===
```

```python
def solve_captcha (driver) :
```

```javascript
    """
```

```javascript
    Send the CAPTCHA challenge to a solver service and return the solution token.
```

```javascript
    This example assumes a token‑based solver (reCAPTCHA v2/v3) .
```

```javascript
    """
```

```typescript
    # Step 1 : Extract the site key ( for reCAPTCHA) – adjust selector as needed
```

```javascript
    site_key = None
```

```javascript
    try :
```

```javascript
        # Look for the data-sitekey attribute on a div or iframe
```

```javascript
        captcha_element = driver. find_element (By. CSS_SELECTOR , ".g-recaptcha" )
```

```javascript
        site_key = captcha_element. get_attribute ( "data-sitekey" )
```

```javascript
    except :
```

```javascript
        # Fallback: some pages use a different structure
```

```javascript
        try :
```

```javascript
            iframe = driver. find_element (By. CSS_SELECTOR , "iframe[src*='recaptcha']" )
```

```javascript
            # You may need to parse the src URL to get site key, but simplified here
```

```javascript
            pass
```

```javascript
        except :
```

```javascript
            pass
```

```javascript
    if not site_key:
```

```javascript
        raise Exception ( "Could not find site key for CAPTCHA" )
```

```javascript
    # Step 2 : Send the challenge to the solver service
```

```javascript
    payload = {
```

```typescript
        "key" : CAPTCHA_SOLVER_API_KEY ,
```

```javascript
        "method" : "userrecaptcha" ,
```

```typescript
        "googlekey" : site_key,
```

```typescript
        "pageurl" : driver.current_url,
```

```javascript
        "json" : 1
```

```javascript
    }
```

```javascript
    response = requests. post ( CAPTCHA_SOLVER_URL , data=payload)
```

```javascript
    result = response. json ( )
```

```javascript
    if result. get ( "status" ) != 1 :
```

```javascript
        raise Exception (f"Solver submission failed: {result}" )
```

```javascript
    request_id = result. get ( "request" )
```

```python
    print ( f"CAPTCHA submitted, request ID:  {request_id} " )
```

```typescript
    # Step 3 : Poll for the solution (wait up to 60 seconds)
```

```javascript
    for _ in range ( 30 ) :  # 30 * 2 seconds = 60 seconds max
```

```javascript
        time. sleep ( 2 )
```

```javascript
        poll_payload = {
```

```typescript
            "key" : CAPTCHA_SOLVER_API_KEY ,
```

```javascript
            "action" : "get" ,
```

```typescript
            "id" : request_id,
```

```javascript
            "json" : 1
```

```javascript
        }
```

```javascript
        poll_resp = requests. get ( CAPTCHA_SOLVER_RESULT_URL , params=poll_payload)
```

```javascript
        poll_data = poll_resp. json ( )
```

```javascript
        if poll_data. get ( "status" ) == 1 :
```

```javascript
            token = poll_data. get ( "request" )
```

```python
            print ( f"CAPTCHA solved, token received:  {token[ : 20] } ..." )
```

```javascript
            return token
```

```javascript
        elif poll_data. get ( "request" ) == "CAPCHA_NOT_READY" :
```

```javascript
            continue
```

```javascript
        else :
```

```javascript
            raise Exception (f"Solver error: {poll_data}" )
```

```javascript
    raise Exception ( "Timeout waiting for CAPTCHA solution" )
```

```javascript
# === INJECT THE SOLUTION ===
```

```python
def inject_captcha_token (driver, token) :
```

```python
    """Inject the solution token into the page and submit."""
```

```javascript
    # For reCAPTCHA, the token is usually injected into a hidden textarea
```

```javascript
    try :
```

```javascript
        driver. execute_script (f"document.getElementById('g-recaptcha-response').innerHTML = '{token}';" )
```

```javascript
    except :
```

```javascript
        pass
```

```javascript
    # Then submit the form (or call the callback)
```

```javascript
    try :
```

```javascript
        submit_button = driver. find_element (By. CSS_SELECTOR , "button[type='submit'], input[type='submit']" )
```

```javascript
        submit_button. click ( )
```

```javascript
    except :
```

```javascript
        # Some pages need to trigger the callback manually
```

```javascript
        driver. execute_script ( "onSuccessCaptcha(arguments[0]);" , token)
```

```javascript
    time. sleep ( 3 )  # wait for page to process
```

```javascript
# === MAIN WORKFLOW WITH FALLBACK ===
```

```python
def scrape_with_captcha_fallback (driver) :
```

```javascript
    """
```

```javascript
    Perform your scraping. If a CAPTCHA appears, solve it and continue .
```

```javascript
    """
```

```javascript
    # ... your navigation and interaction code ...
```

```javascript
    # After each major action, check for CAPTCHA
```

```javascript
    if is_captcha_present (driver) :
```

```python
        print ( "CAPTCHA detected. Attempting to solve..." )
```

```javascript
        try :
```

```javascript
            solution = solve_captcha (driver)
```

```javascript
            inject_captcha_token (driver, solution)
```

```javascript
            # Wait for the page to reload and re-check
```

```javascript
            time. sleep ( 5 )
```

```javascript
            if is_captcha_present (driver) :
```

```python
                print ( "CAPTCHA still present after solving. Might need a retry." )
```

```javascript
                return False
```

```javascript
            else :
```

```python
                print ( "CAPTCHA solved successfully. Continuing." )
```

```javascript
        except Exception as e :
```

```python
            print ( f"CAPTCHA solving failed:  {e} " )
```

```javascript
            return False
```

```javascript
    # Proceed with scraping
```

```python
    print ( "No CAPTCHA blocking – extracting data..." )
```

```javascript
    # ... your data extraction ...
```

```javascript
    return True
```

```javascript
# === EXAMPLE USAGE ===
```

```javascript
if __name__ == "__main__" :
```

```javascript
    # Assume driver is already set up with stealth options
```

```python
    from your_stealth_setup import launch_stealth_browser  # hypothetical import
```

```javascript
    driver = launch_stealth_browser ( )
```

```javascript
    driver. get ( "https://example.com/page-that-may-show-captcha" )
```

```javascript
    success = scrape_with_captcha_fallback (driver)
```

```javascript
    if not success:
```

```python
        print ( "Failed to bypass CAPTCHA. Consider rotating identity or proxy." )
```

```javascript
    driver. quit ( )
```

### Measure CAPTCHA rates and failures

At some point, even the best Selenium script is going to struggle with CAPTCHA prompts, especially if you're running your script at scale. That's why keeping an eye on failure rates is important. By tracking this data, you can fine-tune your retry logic and work out where your setup is letting you down.

The code snippet below logs each request: timestamp, the URL, whether a CAPTCHA appeared, and if so, whether the solver was successful, or if there was an error. It stores all this in a CSV file so you can go back and take a closer look later.

```python
import csv
```

```python
import time
```

```python
import random
```

```python
from datetime import datetime
```

```python
class CAPTCHAMetrics :
```

```python
    def __init__ (self, filename= "captcha_metrics.csv" ) :
```

```javascript
        self.filename = filename
```

```javascript
        self.stats = {
```

```javascript
            "total_requests" : 0 ,
```

```javascript
            "captcha_encountered" : 0 ,
```

```javascript
            "captcha_solved" : 0 ,
```

```javascript
            "captcha_failed" : 0 ,
```

```javascript
            "other_errors" : 0
```

```javascript
        }
```

```javascript
        # Write header if file doesn't exist
```

```javascript
        try :
```

```javascript
            with open (self.filename, 'x' ) as f :
```

```javascript
                writer = csv. writer (f)
```

```javascript
                writer. writerow ( [ "timestamp" , "url" , "captcha_detected" , "solver_used" , 
```

```javascript
                                 "solver_success" , "time_to_solve_sec" , "error_message" ] )
```

```javascript
        except FileExistsError:
```

```javascript
            pass
```

```javascript
    def log_request (self, url, captcha_detected=False, solver_used=False, 
```

```javascript
                    solver_success=False, time_to_solve=None, error=None) :
```

```javascript
        self.stats[ "total_requests" ] += 1
```

```javascript
        if captcha_detected :
```

```javascript
            self.stats[ "captcha_encountered" ] += 1
```

```javascript
            if solver_success :
```

```javascript
                self.stats[ "captcha_solved" ] += 1
```

```javascript
            else :
```

```javascript
                self.stats[ "captcha_failed" ] += 1
```

```javascript
        if error :
```

```javascript
            self.stats[ "other_errors" ] += 1
```

```javascript
        with open (self.filename, 'a' , newline= '' ) as f :
```

```javascript
            writer = csv. writer (f)
```

```javascript
            writer. writerow ( [
```

```javascript
                datetime. now ( ) . isoformat ( ) ,
```

```javascript
                url,
```

```javascript
                captcha_detected,
```

```javascript
                solver_used,
```

```javascript
                solver_success,
```

```javascript
                time_to_solve if time_to_solve else "" ,
```

```javascript
                error if error else ""
```

```javascript
            ] )
```

```python
    def get_captcha_rate (self) :
```

```javascript
        if self.stats[ "total_requests" ] == 0 :
```

```javascript
            return 0
```

```javascript
        return (self.stats[ "captcha_encountered" ] / self.stats[ "total_requests" ] ) * 100
```

```python
    def print_summary (self) :
```

```python
        print ( "\n=== CAPTCHA Metrics Summary ===" )
```

```python
        print ( f"Total requests:  {self.stats[ 'total_requests' ] } " )
```

```python
        print ( f"CAPTCHA encountered:  {self.stats[ 'captcha_encountered' ] } " )
```

```python
        print ( f"CAPTCHA solved:  {self.stats[ 'captcha_solved' ] } " )
```

```python
        print ( f"CAPTCHA failed:  {self.stats[ 'captcha_failed' ] } " )
```

```python
        print ( f"Other errors:  {self.stats[ 'other_errors' ] } " )
```

```python
        print ( f"CAPTCHA rate:  {self.get_captcha_rate( ) : .2f } %" )
```

```javascript
        if self.stats[ 'captcha_encountered' ] > 0 :
```

```javascript
            solve_rate = (self.stats[ 'captcha_solved' ] / self.stats[ 'captcha_encountered' ] ) * 100
```

```python
            print ( f"Solve success rate:  {solve_rate: .2f } %" )
```

```python
        print ( "=============================\n" )
```

## Selenium CAPTCHA troubleshooting

| Problem | Solution |
| --- | --- |
| CAPTCHA only appears in headless mode | Stay in headed mode for critical sites. If headless is required, use --headless=new (Chrome 109+), add a virtual display like Xvfb, and inject CDP scripts to override WebGL and canvas fingerprints. |
| CAPTCHA increases after scaling requests | Add random delays between requests. Spread workload across more IPs. Reduce per-IP request rate. Check if CAPTCHAs appear immediately (fingerprint issue) or after a few minutes (rate limit). |
| Solver returns token, but the challenge still fails | Manually solve the CAPTCHA while watching browser devtools. Note which fields change and which JavaScript functions run. Replicate those exact steps in your script. Test with headed mode first. |
| Proxy rotation makes things worse | Rotate the entire identity together – user agent, viewport, timezone, language, WebGL strings – not just the IP. Keep the same IP and fingerprint combination for several requests before rotating. |
| Browser looks normal locally, but fails in production | Use residential proxies on your production server. Ensure the server has common fonts installed. Run headed mode with a virtual display like Xvfb, or inject CDP scripts to fake missing GPU features. |

## Conclusion

Bypassing CAPTCHA with Selenium is a step-by-step process. First, try to avoid CAPTCHAs altogether. Use undetected-chromedriver for a stealthier browser setup and add human behavior techniques like random waits, varied scrolling and exponential backoff when retries fail.

Next, add proxies and make sure each IP address has its own complete identity profile, and rotate the whole set together, not just the IP alone. When all else fails, bring in a solver service as a fallback. Your script should detect the CAPTCHA, send it to the API, and inject the returned token.

And finally, track everything. Measure your CAPTCHA rate and failure patterns because, without data, you can’t tell which part of your setup needs attention.

Looking for more tips like this? Join our [Discord channel](https://discord.gg/YJWrbVq8at/).

## FAQ

Can Selenium bypass CAPTCHA by itself?

No, Selenium alone can’t bypass CAPTCHA. A standard Selenium WebDriver session actually triggers more CAPTCHAs because it leaks automation signals like navigator.webdriver and missing Chrome objects. You need more.

Does undetected-chromedriver prevent CAPTCHA in Selenium?

It reduces CAPTCHA rates but doesn’t prevent them. undetected-chromedriver hides many automation fingerprints like the cdc\_ string and navigator.webdriver flag. But behavioral patterns and IP reputation still trigger challenges sometimes.

Why does CAPTCHA appear more often in headless mode?

Headless mode uses software rendering like SwiftShader instead of a real GPU. It also defaults to 800x600 window size and misses normal fonts. Websites detect these differences and serve more CAPTCHAs to headless browsers.

What is the best proxy type for Selenium CAPTCHA issues?

Residential proxies are best because they come from real ISP addresses, not datacenter ranges. Datacenter IPs are easily flagged as non-residential traffic. Mobile proxies also work well, but cost more and have different geolocation characteristics.

When should I use a CAPTCHA solver with Selenium?

Use a solver only as a fallback after stealth setup, human behavior patterns, and residential proxies have failed. Don’t rely on solvers first - they add latency and cost. Reserve them for occasional challenges that bypass your primary defenses.

[Learn more](https://dashboard.marsproxies.com/register/)

\-

Author

[Dalius Pamedytis Software Engineer](https://marsproxies.com/blog/authors/dalius-pamedytis/)

Table of contents

1. [Why Selenium triggers CAPTCHA](https://marsproxies.com/blog/bypass-captcha-selenium/#b-why-selenium-triggers-captcha)
2. [Method 1: Use a stealthier Selenium browser setup](https://marsproxies.com/blog/bypass-captcha-selenium/#b-method-1-use-a-stealthier-selenium-browser-setup)
3. [undetected-chromedriver](https://marsproxies.com/blog/bypass-captcha-selenium/#b-undetected-chromedriver)
4. [Remove obvious automation signals](https://marsproxies.com/blog/bypass-captcha-selenium/#b-remove-obvious-automation-signals)
5. [Method 2: Improve IP reputation with proxies](https://marsproxies.com/blog/bypass-captcha-selenium/#b-method-2-improve-ip-reputation-with-proxies)
6. [Residential vs datacenter vs mobile proxies](https://marsproxies.com/blog/bypass-captcha-selenium/#b-residential-vs-datacenter-vs-mobile-proxies)
7. [Rotating vs sticky sessions](https://marsproxies.com/blog/bypass-captcha-selenium/#b-rotating-vs-sticky-sessions)
8. [Method 3: Improve session and behavior management](https://marsproxies.com/blog/bypass-captcha-selenium/#b-method-3-improve-session-and-behavior-management)
9. [Reuse those cookies and sessions](https://marsproxies.com/blog/bypass-captcha-selenium/#b-reuse-those-cookies-and-sessions)
10. [Avoid repeated cold starts](https://marsproxies.com/blog/bypass-captcha-selenium/#b-avoid-repeated-cold-starts)
11. [Use realistic waits and navigation patterns](https://marsproxies.com/blog/bypass-captcha-selenium/#b-use-realistic-waits-and-navigation-patterns)
12. [Lower request volume and retry spikes](https://marsproxies.com/blog/bypass-captcha-selenium/#b-lower-request-volume-and-retry-spikes)
13. [Method 4: Use a CAPTCHA solver service](https://marsproxies.com/blog/bypass-captcha-selenium/#b-method-4-use-a-captcha-solver-service)
14. [Token-based vs interactive solving](https://marsproxies.com/blog/bypass-captcha-selenium/#b-token-based-vs-interactive-solving)
15. [Supported challenge types](https://marsproxies.com/blog/bypass-captcha-selenium/#b-supported-challenge-types)
16. [The trade-offs of using CAPTCHA solvers](https://marsproxies.com/blog/bypass-captcha-selenium/#b-the-trade-offs-of-using-captcha-solvers)
17. [A step-by-step workflow for CAPTCHA in Selenium](https://marsproxies.com/blog/bypass-captcha-selenium/#b-a-step-by-step-workflow-for-captcha-in-selenium)
18. [Getting started with a stealth browser setup](https://marsproxies.com/blog/bypass-captcha-selenium/#b-getting-started-with-a-stealth-browser-setup)
19. [Add behavior tuning and session persistence](https://marsproxies.com/blog/bypass-captcha-selenium/#b-add-behavior-tuning-and-session-persistence)
20. [Add proxies if CAPTCHA still appears](https://marsproxies.com/blog/bypass-captcha-selenium/#b-add-proxies-if-captcha-still-appears)
21. [Add a solver only when needed](https://marsproxies.com/blog/bypass-captcha-selenium/#b-add-a-solver-only-when-needed)
22. [Measure CAPTCHA rates and failures](https://marsproxies.com/blog/bypass-captcha-selenium/#b-measure-captcha-rates-and-failures)
23. [Selenium CAPTCHA troubleshooting](https://marsproxies.com/blog/bypass-captcha-selenium/#b-selenium-captcha-troubleshooting)
24. [Conclusion](https://marsproxies.com/blog/bypass-captcha-selenium/#b-conclusion)
25. [FAQ](https://marsproxies.com/blog/bypass-captcha-selenium/#b-faq)

Categories

- [Antidetect solutions](https://marsproxies.com/blog/category/antidetect-solution/)

  [3](https://marsproxies.com/blog/category/antidetect-solution/)
- [Comparisons](https://marsproxies.com/blog/category/comparisons/)

  [9](https://marsproxies.com/blog/category/comparisons/)
- [How to](https://marsproxies.com/blog/category/how-to/)

  [127](https://marsproxies.com/blog/category/how-to/)
- [Insights](https://marsproxies.com/blog/category/insights/)

  [89](https://marsproxies.com/blog/category/insights/)
- [Product news](https://marsproxies.com/blog/category/product-news/)

  [5](https://marsproxies.com/blog/category/product-news/)
- [Sneaker Bots](https://marsproxies.com/blog/category/sneaker-bots/)

  [2](https://marsproxies.com/blog/category/sneaker-bots/)

## Related articles

[How to](https://marsproxies.com/blog/category/how-to/)

### [Twitter Scraper API: The Best Tools & How to Use Them](https://marsproxies.com/blog/twitter-scraper-api/)

Learn how a Twitter scraper API works, compare the best tools, and discover how to scrape Twitter data with practical examples, tips, and best practices.

[Dalius Pamedytis Software Engineer](https://marsproxies.com/blog/authors/dalius-pamedytis/)

October 29, 2026

[How to](https://marsproxies.com/blog/category/how-to/)

### [How to Watch Japanese TV in the USA: 2026 Guide](https://marsproxies.com/blog/watch-japanese-tv-in-usa/)

Discover the best ways to watch Japanese TV in the USA, including free streaming platforms, VPN setup guides, and subscription services.

[Lukas Tilkevicius Growth & Partnerships Manager](https://marsproxies.com/blog/authors/lukas-tilkevicius/)

October 28, 2026

[How to](https://marsproxies.com/blog/category/how-to/)

### [What is AI Scraping & How Does It Work?](https://marsproxies.com/blog/ai-scraping/)

Learn what AI scraping is, how AI web scrapers extract and understand website data, how they differ from traditional scrapers, and where proxies fit in.

[Dalius Pamedytis Software Engineer](https://marsproxies.com/blog/authors/dalius-pamedytis/)

October 27, 2026
