Cloudflare Turnstile CAPTCHA: How to Solve It in 2026
Cloudflare Turnstile is the company’s replacement for traditional CAPTCHAs. Unlike reCAPTCHA or hCaptcha, Turnstile aims to verify humans without visible challenges — making it harder for scrapers to detect when they’re being tested and even harder to solve programmatically.
If your scraper is hitting Turnstile challenges, this guide covers every viable method to solve them in 2026, from browser automation to third-party solving services.
What Is Cloudflare Turnstile?
Turnstile is a standalone CAPTCHA alternative that Cloudflare offers to any website, not just those using Cloudflare’s CDN. It works by running a series of browser environment checks and behavioral signals in the background. When it’s confident the visitor is human, it issues a cf-turnstile-response token that the site’s backend validates.
There are three modes:
- Managed — Cloudflare decides whether to show a visible challenge or pass silently
- Non-interactive — Always invisible; relies entirely on passive signals
- Invisible — Similar to non-interactive but can escalate to a visible challenge
How Turnstile Differs from JS Challenges
Cloudflare’s standard JavaScript challenge (the “Checking your browser” page) protects the entire site at the CDN level. Turnstile is embedded as a widget on specific pages — typically login forms, checkout pages, or search endpoints. The site developer places it explicitly using a
This means you might access most of a site without issues but hit Turnstile on the specific pages that matter most.
Detecting Turnstile on a Page
Before trying to solve Turnstile, you need to identify it. Look for these indicators in the page source:
from playwright.sync_api import sync_playwright
def detect_turnstile(url):
with sync_playwright() as p:
browser = p.chromium.launch(headless=False)
page = browser.new_page()
page.goto(url, wait_until="networkidle")
# Check for Turnstile widget
turnstile_div = page.query_selector(".cf-turnstile")
turnstile_script = page.query_selector(
'script[src*="challenges.cloudflare.com/turnstile"]'
)
if turnstile_div:
site_key = turnstile_div.get_attribute("data-sitekey")
print(f"Turnstile detected! Site key: {site_key}")
elif turnstile_script:
print("Turnstile script loaded but widget not yet rendered")
else:
print("No Turnstile detected")
browser.close()
detect_turnstile("https://example.com/login")The data-sitekey attribute is critical — you’ll need it for most solving methods.
Method 1: Browser Automation with Stealth
The most reliable approach is using a real browser with anti-detection patches. Since Turnstile relies heavily on browser environment checks, a properly configured browser often passes silently.
Playwright with Stealth
from playwright.sync_api import sync_playwright
import time
def solve_turnstile_playwright(url):
with sync_playwright() as p:
browser = p.chromium.launch(
headless=False,
args=[
"--disable-blink-features=AutomationControlled",
"--no-sandbox",
]
)
context = browser.new_context(
viewport={"width": 1920, "height": 1080},
user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/122.0.0.0 Safari/537.36",
locale="en-US",
)
# Remove webdriver flag
context.add_init_script("""
Object.defineProperty(navigator, 'webdriver', {
get: () => undefined
});
""")
page = context.new_page()
page.goto(url, wait_until="networkidle")
# Wait for Turnstile to process
# In managed mode, it may auto-solve
try:
page.wait_for_function(
"""() => {
const input = document.querySelector(
'input[name="cf-turnstile-response"]'
);
return input && input.value.length > 0;
}""",
timeout=15000
)
token = page.evaluate(
'document.querySelector('
'"input[name=\\'cf-turnstile-response\\']").value'
)
print(f"Turnstile solved! Token: {token[:50]}...")
return token
except Exception:
# May need to click the checkbox
checkbox = page.frame_locator(
'iframe[src*="challenges.cloudflare.com"]'
).locator(".mark")
if checkbox.is_visible():
checkbox.click()
time.sleep(3)
browser.close()
solve_turnstile_playwright("https://example.com/login")Using Undetected ChromeDriver
For Selenium users, undetected-chromedriver patches Chrome to avoid common detection vectors:
import undetected_chromedriver as uc
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
driver = uc.Chrome(headless=False, version_main=122)
driver.get("https://example.com/login")
# Wait for Turnstile token to be generated
try:
WebDriverWait(driver, 20).until(
lambda d: d.execute_script(
'return document.querySelector('
'"input[name=\\'cf-turnstile-response\\']")?.value?.length > 0'
)
)
token = driver.execute_script(
'return document.querySelector('
'"input[name=\\'cf-turnstile-response\\']").value'
)
print(f"Token obtained: {token[:50]}...")
except Exception as e:
print(f"Turnstile not solved: {e}")
finally:
driver.quit()Method 2: CAPTCHA Solving Services
When browser automation isn’t practical at scale, CAPTCHA solving services can handle Turnstile. These services accept the site key and URL, then return a valid token.
Using 2Captcha
import requests
import time
API_KEY = "your_2captcha_api_key"
SITE_KEY = "0x4AAAAAAXXXXXXXXXXXXXXX" # From data-sitekey
PAGE_URL = "https://example.com/login"
def solve_turnstile_2captcha(api_key, site_key, page_url):
# Submit task
payload = {
"key": api_key,
"method": "turnstile",
"sitekey": site_key,
"pageurl": page_url,
"json": 1
}
resp = requests.post(
"https://2captcha.com/in.php", data=payload
).json()
if resp["status"] != 1:
raise Exception(f"Submit failed: {resp}")
task_id = resp["request"]
print(f"Task submitted: {task_id}")
# Poll for result
for _ in range(30):
time.sleep(5)
result = requests.get(
"https://2captcha.com/res.php",
params={
"key": api_key,
"action": "get",
"id": task_id,
"json": 1
}
).json()
if result["status"] == 1:
return result["request"]
elif result["request"] != "CAPCHA_NOT_READY":
raise Exception(f"Solve failed: {result}")
raise TimeoutError("Solving timed out")
token = solve_turnstile_2captcha(API_KEY, SITE_KEY, PAGE_URL)Injecting the Token
Once you have a token, inject it into the form submission:
import requests
session = requests.Session()
# Use the token in the form POST
login_data = {
"username": "user@example.com",
"password": "password123",
"cf-turnstile-response": token
}
response = session.post(
"https://example.com/api/login",
data=login_data,
headers={
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 Chrome/122.0.0.0 Safari/537.36",
"Origin": "https://example.com",
"Referer": "https://example.com/login"
}
)
print(response.status_code)Method 3: Residential Proxies to Reduce Challenge Rate
Turnstile’s behavior varies based on IP reputation. Requests from datacenter IPs are far more likely to trigger visible challenges. Residential proxies route traffic through real ISP connections, which typically get the invisible (non-interactive) mode.
import requests
proxy_config = {
"http": "http://user:pass@residential-gateway.example.com:7777",
"https": "http://user:pass@residential-gateway.example.com:7777"
}
session = requests.Session()
session.proxies = proxy_config
session.headers.update({
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 Chrome/122.0.0.0 Safari/537.36",
"Accept-Language": "en-US,en;q=0.9",
})
response = session.get("https://target-site.com/search?q=data")Combining residential proxies with browser automation gives the highest success rate. The proxy handles IP reputation while the browser handles environment checks.
Method 4: Using FlareSolverr
FlareSolverr is a proxy server that uses a real browser under the hood. While primarily designed for Cloudflare JS challenges, it can handle Turnstile on pages where Turnstile is part of the Cloudflare challenge flow.
import requests
flaresolverr_url = "http://localhost:8191/v1"
payload = {
"cmd": "request.get",
"url": "https://target-site.com/protected-page",
"maxTimeout": 60000
}
response = requests.post(flaresolverr_url, json=payload)
data = response.json()
if data["status"] == "ok":
html = data["solution"]["response"]
cookies = data["solution"]["cookies"]
print(f"Page fetched, {len(html)} bytes")Turnstile Token Lifecycle
Understanding token expiry is important for scaling:
| Aspect | Detail |
|---|---|
| Token validity | ~300 seconds (5 minutes) |
| Single use | Yes — each token works once |
| Domain locked | Token is bound to the site key’s domain |
| IP binding | Some implementations verify the solving IP matches the submission IP |
If the site binds tokens to IPs, you must solve and submit from the same proxy IP. This rules out using external solving services in some cases.
Handling Turnstile at Scale
For high-volume scraping against Turnstile-protected endpoints:
- Pre-solve tokens — Maintain a pool of valid tokens solved ahead of time, consuming them within the 5-minute window
- Rotate browser profiles — Each browser session should have unique fingerprints
- Use sticky sessions — Keep the same proxy IP for solving and submitting
- Monitor success rates — Track token acceptance rate and adjust strategies
import queue
import threading
import time
token_pool = queue.Queue(maxsize=20)
def token_worker(site_key, page_url):
"""Background worker that keeps the token pool filled."""
while True:
if token_pool.qsize() < 10:
try:
token = solve_turnstile_2captcha(
API_KEY, site_key, page_url
)
token_pool.put(
{"token": token, "created": time.time()}
)
except Exception as e:
print(f"Solve error: {e}")
time.sleep(2)
def get_fresh_token(max_age=240):
"""Get a token that's less than 4 minutes old."""
while True:
item = token_pool.get()
if time.time() - item["created"] < max_age:
return item["token"]
# Token too old, discard and get nextCommon Pitfalls
- Headless mode detection — Turnstile can detect headless browsers. Use
headless=Falseor the new headless mode in Chrome 122+ - Missing mouse movement — Some Turnstile configurations score based on input events before the widget loads
- Iframe interaction — The Turnstile widget lives inside a cross-origin iframe, so direct DOM manipulation doesn’t work
- Token reuse — Tokens are single-use; submitting the same token twice will fail
FAQ
Can Cloudflare Turnstile be solved without a browser?
Not directly. Turnstile requires a browser environment to generate its token. However, you can use CAPTCHA solving services that run browsers on their end and return the token for you to use in plain HTTP requests.
How much does it cost to solve Turnstile at scale?
CAPTCHA solving services charge $2-6 per 1,000 Turnstile solves. Browser-based solutions have infrastructure costs but avoid per-solve fees. For high-volume operations, running your own browser cluster with residential proxies is typically more cost-effective.
Does Turnstile work on non-Cloudflare sites?
Yes. Any website can embed Cloudflare Turnstile regardless of whether they use Cloudflare’s CDN. The widget is loaded from challenges.cloudflare.com and works independently of the site’s infrastructure.
What’s the difference between Turnstile and the Cloudflare JS challenge?
The JS challenge is a full-page interstitial that Cloudflare injects at the CDN level, blocking access to the entire site. Turnstile is a widget embedded on specific pages by the site developer, typically protecting forms or API endpoints.
Can I use the same solving approach for Turnstile and reCAPTCHA?
The concepts are similar — both require tokens that get submitted with forms. However, the technical implementation differs. CAPTCHA solving services support both but use different API methods. Browser automation works for both, but the detection vectors are different. See our guide on bypassing reCAPTCHA for reCAPTCHA-specific techniques.
Conclusion
Cloudflare Turnstile is one of the more sophisticated bot detection widgets in 2026, but it’s not unbeatable. Browser automation with proper stealth configuration remains the most reliable method, especially when combined with residential proxies. For scale, CAPTCHA solving services provide a straightforward API-based approach.
The key is matching your method to your use case: browser automation for quality, solving services for volume, and always residential proxies for IP reputation.
Useful Resources
- Cloudflare Turnstile Documentation
- How Websites Detect Bots
- Browser Fingerprinting Guide
- Best CAPTCHA Solving Services
- 403 Forbidden in Web Scraping: How to Fix It
- Best CAPTCHA Solving Services in 2026: Complete Comparison
- Anti-Phishing with Proxies: How Security Teams Use Mobile IPs
- Brand Protection with Proxies: Detect Counterfeit Sellers & Trademark Violations
- How Cybersecurity Teams Use Proxies for Threat Intelligence
- Using Mobile Proxies for Dark Web Monitoring and Research
- 403 Forbidden in Web Scraping: How to Fix It
- Best CAPTCHA Solving Services in 2026: Complete Comparison
- Anti-Phishing with Proxies: How Security Teams Use Mobile IPs
- Brand Protection with Proxies: Detect Counterfeit Sellers & Trademark Violations
- How Cybersecurity Teams Use Proxies for Threat Intelligence
- Using Mobile Proxies for Dark Web Monitoring and Research
- 403 Forbidden in Web Scraping: How to Fix It
- Best CAPTCHA Solving Services in 2026: Complete Comparison
- Anti-Phishing with Proxies: How Security Teams Use Mobile IPs
- Brand Protection with Proxies: Detect Counterfeit Sellers & Trademark Violations
- How Cybersecurity Teams Use Proxies for Threat Intelligence
- Using Mobile Proxies for Dark Web Monitoring and Research
Related Reading
- 403 Forbidden in Web Scraping: How to Fix It
- Best CAPTCHA Solving Services in 2026: Complete Comparison
- Anti-Phishing with Proxies: How Security Teams Use Mobile IPs
- Brand Protection with Proxies: Detect Counterfeit Sellers & Trademark Violations
- How Cybersecurity Teams Use Proxies for Threat Intelligence
- Using Mobile Proxies for Dark Web Monitoring and Research