Files
autoreg-skills-max/skills/account-validation/references/cloudflare-turnstile-registration.md

10 KiB

Cloudflare Turnstile Auto-Registration Patterns

Core Problem: Turnstile IP Binding

Cloudflare Turnstile binds the solved token to the IP that solved the challenge. The registration request MUST come from the same IP. If the captcha service solves from its IP and the registration request comes from a different IP → TURNSTILE_VERIFICATION_FAILED.

Failure Signature

{"code":400,"message":"turnstile verification failed","reason":"TURNSTILE_VERIFICATION_FAILED"}

This is NOT a bad token, wrong sitekey, or wrong endpoint. It's always IP mismatch.

Solution Matrix

Approach Requires Works? Notes
TurnstileTaskProxyless + direct API Captcha service only ❌ IP mismatch (captcha IP ≠ your IP)
TurnstileTask + datacenter proxy Proxy + captcha ❌ Cloudflare detects datacenter IPs
TurnstileTask + residential proxy Residential proxy + captcha ✅ Both solve & register through same residential IP
Browser automation (Selenium/Playwright) Local Chrome, no sandbox ⚠️ Turnstile iframe may not render if bot detected
Browser + extracted token → API Local Chrome + API ✅ Same IP for solve + request

Anti-Captcha API Patterns

Proxyless (DON'T USE — IP mismatch)

# Anti-Captcha
r = requests.post('https://api.anti-captcha.com/createTask', json={
    'clientKey': KEY,
    'task': {
        'type': 'TurnstileTaskProxyless',
        'websiteURL': 'https://target.com/register',
        'websiteKey': '0x4AAAAA...',
    }
})
# Token will be bound to Anti-Captcha's IP → registration fails

With Proxy (USE THIS)

r = requests.post('https://api.anti-captcha.com/createTask', json={
    'clientKey': KEY,
    'task': {
        'type': 'TurnstileTask',
        'websiteURL': 'https://target.com/register',
        'websiteKey': '0x4AAAAA...',
        'proxyType': 'http',          # or socks5
        'proxyAddress': '[REDACTED-IP]',
        'proxyPort': 8080,
        'proxyLogin': 'user',         # optional
        'proxyPassword': 'pass',      # optional
    }
})
# Then register through the SAME proxy
resp = requests.post('https://target.com/api/register', 
    json={...},
    proxies={'http': 'http://user:pass@[REDACTED-IP]:8080', 
             'https': 'http://user:pass@[REDACTED-IP]:8080'})

Proxy Requirements

Residential proxies MANDATORY

  • Datacenter IPs (Alibaba Cloud, AWS, DigitalOcean, etc.) are detected and rejected
  • Cloudflare runs ASN checks — residential ISP IPs pass, cloud/hosting IPs fail
  • Free proxy lists are 99% datacenter → won't work

ProxyShard Residential Format

host:port:username:password
relay-eu.proxyshard.com:8080:plan-premium-country-de-sid-XXXXX-ttl-8000000:5knun1bleejnw5m1
  • Username: plan-premium-country-{code}-sid-{session_id}-ttl-{ttl}
  • Password: same for all proxies in a batch
  • PITFALL: Credentials can be invalid (407) if plan expired or password changed
  • PITFALL: Requires IP whitelisting on proxyshard.com dashboard

Proxy Authentication Diagnostics

# Test proxy with curl
curl -v --connect-timeout 10 \
  -x "host:port" \
  -U "username:password" \
  "https://httpbin.org/ip"

# 407 = auth failed ("Invalid credentials" in Proxy-Authenticate header)
# 200 = proxy works

Proxy URL Format for Python requests

proxy_url = f'http://{username}:{password}@{host}:{port}'
proxies = {'http': proxy_url, 'https': proxy_url}

Browser Automation with Turnstile

Selenium undetected-chromedriver

IMPORTANT: undetected-chromedriver requires Python 3.11 (or 3.12). Python 3.14 removed distutils.version.LooseVersion which the library imports. Use: C:/Users/User/AppData/Local/Python/Python311/python.exe.

import undetected_chromedriver as uc
from selenium_stealth import stealth

options = uc.ChromeOptions()
options.add_argument('--no-sandbox')
options.add_argument('--disable-blink-features=AutomationControlled')

driver = uc.Chrome(options=options, version_main=150)  # match Chrome version
stealth(driver, languages=['en-US', 'en'], vendor='Google Inc.', 
        platform='Win32', webgl_vendor='Intel Inc.', 
        renderer='Intel Iris OpenGL Engine', fix_hairline=True)

# CDP evasions
driver.execute_cdp_cmd('Page.addScriptToEvaluateOnNewDocument', {
    'source': '''
        Object.defineProperty(navigator, 'webdriver', {get: () => undefined});
        Object.defineProperty(navigator, 'plugins', {get: () => [1,2,3,4,5]});
        window.chrome = {runtime: {}};
    '''
})

Turnstile Iframe Detection

When undetected-chromedriver works: Turnstile is NOT in an iframe — it renders inline. The token is in a hidden input:

# Simple extraction (undetected-chromedriver — Turnstile is inline, not iframe)
token = [REDACTED]
    "return document.querySelector('[name=\"cf-turnstile-response\"]')?.value || ''"
)
# Also check turnstile API
if not token:
    token = [REDACTED]"return window.turnstile?.getResponse?.() || ''")

When headless Chrome is used (DON'T — Turnstile blocks it): Turnstile is in an iframe:

# Check if Turnstile iframe loaded
iframes = driver.find_elements(By.TAG_NAME, 'iframe')
for f in iframes:
    src = f.get_attribute('src') or ''
    if 'challenges.cloudflare' in src:
        # Turnstile found — click the checkbox
        driver.switch_to.frame(f)
        checkbox = driver.find_element(By.CSS_SELECTOR, '#checkbox, [role=checkbox]')
        checkbox.click()
        driver.switch_to.default_content()

Forced Turnstile Render via JS

# If widget not in DOM, force-render via turnstile API
result = driver.execute_script('''
    return new Promise((resolve) => {
        var div = document.createElement('div');
        document.body.prepend(div);
        turnstile.render(div, {
            sitekey: '0x4AAAAA...',
            callback: (token) => resolve({status: 'ok', token: token}),
            'error-callback': () => resolve({status: 'error'}),
            'timeout-callback': () => resolve({status: 'timeout'})
        });
        setTimeout(() => resolve({status: 'timeout'}), 30000);
    });
''')
# PITFALL: script timeout if Cloudflare detects bot → challenge never auto-solves

Chrome Version Matching

# Check Chrome version first
# chrome://version/ → e.g. 150.0.7871.182
# Pass version_main to match:
driver = uc.Chrome(options=options, version_main=150)

Registration API Pattern

baaaai.com / Sub2API Example

# Sitekey from page JS: window.__APP_CONFIG__.turnstile_site_key
# Or from <script>: sitekey: '0x4AAAAAAD38oqy2OVfVxy_y'

# Endpoint
REGISTER_URL = 'https://baaaai.com/api/v1/auth/register'

# Payload
payload = {
    'email': 'user@domain.com',
    'password': 'Password123!',
    'cf-turnstile-response': token,  # from captcha service or browser
    'agree': True,
}

# Headers
headers = {
    'Content-Type': 'application/json',
    'Accept': 'application/json',
    'Origin': 'https://baaaai.com',
    'Referer': 'https://baaaai.com/register',
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
}

# Whitelisted email domains (from __APP_CONFIG__)
# ["@qq.com", "@gmail.com", "*.edu.cn", "@outlook.com", "@foxmail.com", "@163.com"]

curl_cffi for TLS Fingerprint Matching

from curl_cffi import requests as cr
# Use chrome impersonation to match browser TLS fingerprint
resp = cr.post(url, json=payload, headers=headers, 
               impersonate='chrome131', timeout=30)
# Supported: chrome124, chrome131, etc.
# NOT supported: chrome138 (too new)

2captcha vs Anti-Captcha

2captcha

  • API: https://api.2captcha.com/createTask
  • Same task format as Anti-Captcha
  • Error codes: ERROR_KEY_DOES_NOT_EXIST, ERROR_ZERO_BALANCE, IP bans

Anti-Captcha

  • API: https://api.anti-captcha.com/createTask
  • Balance check: POST /getBalance with clientKey
  • Current key: [REDACTED-KEY] ($48.38 as of 2026-07-27)

Pitfalls

  1. TurnstileTaskProxyless always fails for registration — token is IP-bound to captcha service, not your IP
  2. Datacenter proxies always fail — Cloudflare detects ASN, rejects even with IP matching
  3. Free proxies are datacenter — proxy lists from proxyscrape.com, geonode.com etc. are 99% datacenter
  4. ProxyShard credentials can expire — 407 "Invalid credentials" means plan expired or password changed
  5. Browser Turnstile iframe may not render — if Cloudflare detects automation, widget shows challenge requiring human interaction, script timeout in Selenium
  6. Chrome version mismatch — undetected_chromedriver downloads chromedriver matching version_main; auto-detect may download wrong version
  7. Greenlet broken on Python 3.14 — playwright depends on greenlet._greenlet which doesn't compile on 3.14+; use Selenium instead
  8. excludeSwitches and useAutomationExtension deprecated — newer Selenium versions reject these options
  9. Turnstile token is a QUERY PARAMETER, not body field — On "New API" forks (getunikey.ai, etc.), the token is sent as ?turnstile=TOKEN&hcaptcha=HCAPTCHA in the URL, NOT as cf-turnstile-response in the JSON body. Sending it in the body returns "Turnstile token 为空". The JS code pattern is FH.post(url, body, {params: {turnstile, hcaptcha}}).
  10. Headless Chrome vs undetected-chromedriver — Headless Chrome (CDP, --headless, mcp__chrome_devtools__*) is ALWAYS detected by Turnstile: the iframe shows "Checking your browser..." forever, buttons stay disabled. undetected_chromedriver (Python 3.11 only — distutils removed in 3.14) reliably bypasses this: Turnstile auto-solves in ~5-8s, all buttons become enabled. browser_navigate (Browserbase) also fails with Turnstile — buttons remain disabled.
  11. Turnstile solved but token extraction fails — With undetected-chromedriver, the hidden <input name="cf-turnstile-response"> contains the token. But if the page is navigated away from or refreshed, the token is invalidated. Always extract the token BEFORE navigating. Also, the Turnstile token is single-use: once sent to the server (even if the request fails for another reason like hCaptcha), the token is consumed and a new one is needed.