Solución de Problemas

Bucle del Cloudflare Challenge: por qué se repite y cómo corregirlo

Si el Cloudflare Challenge vuelve a aparecer justo después de resolverlo, el fallo casi nunca está en la resolución: está en el contexto de la sesión. La cookie qa_validation_cookie que devuelve CaptchaAI solo vale mientras la IP de salida, el User-Agent y el dominio sigan siendo los mismos que se usaron al resolver. Si uno de los tres cambia, Cloudflare descarta la cookie y te devuelve al challenge.

Antes de tocar el código, ubica tu caso:

  • ¿Falla solo en CI y no en tu máquina? Sospecha de la IP de salida.
  • ¿Falla al saltar a un subdominio? Sospecha del dominio de la cookie.
  • ¿Falla tras varios minutos funcionando? Sospecha de la caducidad.

Por qué se produce el bucle del Cloudflare Challenge

La cookie no es un pase universal. La qa_validation_cookie que devuelve CaptchaAI queda atada a tres señales que Cloudflare vuelve a comprobar en cada solicitud:

Señal Con qué se compara
IP del proxy La dirección desde la que se resolvió el challenge
User-Agent La cadena exacta enviada al resolver
Dominio El del sitio de destino, subdominios incluidos

El bucle es un síntoma de desincronización, no de una resolución fallida: en tus logs verás resoluciones correctas y, aun así, un challenge en el siguiente GET.


Síntomas y acciones rápidas

Síntoma Acción recomendada
El challenge reaparece tras cada resolución Comprueba que la IP y el User-Agent son idénticos en ambos pasos
ERROR_NO_SLOT_AVAILABLE Reintenta con retroceso exponencial
El backend rechaza el token Compara action/sitekey con tu configuración real
El test funciona en local pero falla en CI Iguala viewport, idioma y user-agent en ambos entornos
Tiempos de resolución muy variables Revisa la concurrencia y los threads de tu plan

Causa 1: el User-Agent cambia entre la resolución y la solicitud

Es la causa más frecuente y la más fácil de pasar por alto: el User-Agent que envías a CaptchaAI debe coincidir carácter por carácter con el que usas después en tu sesión. Basta con que tu librería HTTP ponga el suyo por defecto para que la cookie deje de valer. Define la cadena en una constante y reutilízala en ambos lados.

import requests

USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"

# Step 1: Solve with the SAME User-Agent
solve_data = {
    "key": "YOUR_API_KEY",
    "method": "cloudflare_challenge",
    "pageurl": "https://example.com",
    "proxy": "host:port:user:pass",
    "proxytype": "HTTP",
    "userAgent": USER_AGENT,  # Must match step 2
    "json": 1
}
submit = requests.post("https://ocr.captchaai.com/in.php", data=solve_data).json()

# ... poll for result ...

# Step 2: Use the SAME User-Agent in subsequent requests
session = requests.Session()
session.headers["User-Agent"] = USER_AGENT  # Must match step 1
session.cookies.set("qa_validation_cookie", qa_validation_cookie_value, domain=".example.com")
resp = session.get("https://example.com")

Causa 2: la IP de salida no es la misma que resolvió el challenge

La cookie está vinculada a la IP que completó el challenge: si consultas el sitio desde otra dirección, Cloudflare la rechaza. Pasa constantemente en pipelines distribuidas, donde resuelves desde un worker y navegas desde otro proceso, otro contenedor u otro runner de CI.

PROXY = "host:port:user:pass"

# Solve with this proxy
solve_data = {
    "key": "YOUR_API_KEY",
    "method": "cloudflare_challenge",
    "pageurl": "https://example.com",
    "proxy": PROXY,
    "proxytype": "HTTP",
    "userAgent": USER_AGENT,
    "json": 1
}

# Use the SAME proxy for subsequent requests
session.proxies = {
    "http": f"http://user:pass@host:port",
    "https": f"http://user:pass@host:port"
}

Dos reglas para pipelines distribuidas:

  • Con proxies rotativos, fija una sesión estable mientras la cookie siga viva.
  • La IP debe ser idéntica al resolver y al navegar, sin excepciones.

qa_validation_cookie debe quedar registrada en el dominio correcto y con los atributos correctos. El error típico es guardarla sin el punto inicial: así no cubre los subdominios y cualquier salto a www. vuelve a disparar el challenge.

# WRONG — setting on wrong domain
session.cookies.set("qa_validation_cookie", value, domain="example.com")

# CORRECT — include the dot prefix for subdomain coverage
session.cookies.set("qa_validation_cookie", value, domain=".example.com")

# Or set all cookies returned by CaptchaAI
for cookie_str in result.get("cookies", "").split(";"):
    if "qa_validation_cookie" in cookie_str:
        name, val = cookie_str.strip().split("=", 1)
        session.cookies.set(name.strip(), val.strip(), domain=".example.com")

Estas cookies tienen una vida útil limitada, normalmente de 15 a 30 minutos, y algunos sitios acortan ese margen. Si tu código no lleva la cuenta del tiempo, interpretarás como bucle un challenge perfectamente legítimo.

Solución: controla la antigüedad de la cookie y vuelve a resolver antes del vencimiento, no después del primer error.

import time

last_solve_time = None
COOKIE_TTL = 900  # 15 minutes

def get_qa_validation_cookie():
    global last_solve_time
    if last_solve_time and (time.time() - last_solve_time) < COOKIE_TTL:
        return  # Cookie still valid

    # Solve again
    qa_validation_cookie = solve_cloudflare_challenge()
    session.cookies.set("qa_validation_cookie", qa_validation_cookie, domain=".example.com")
    last_solve_time = time.time()

Causa 5: las señales TLS de tu cliente no parecen las de un navegador

Cloudflare también analiza las señales TLS de la conexión. La librería requests de Python presenta una firma distinta a la de Chrome, y algunos sitios rechazan la solicitud aunque la cookie sea válida. Es el caso más difícil de diagnosticar.

Solución: usa curl_cffi o tls-client, que reproducen el TLS de un navegador real.

pip install curl_cffi
from curl_cffi import requests as curl_requests

session = curl_requests.Session(impersonate="chrome120")
session.cookies.set("qa_validation_cookie", value, domain=".example.com")
resp = session.get("https://example.com")

Checklist para diagnosticar el bucle del Cloudflare Challenge

Recorre las comprobaciones en orden: la mayoría de los casos se cierra en los tres primeros pasos.

Challenge keeps repeating
    ↓
User-Agent in solve request matches browsing request? → No → Sync User-Agent
    ↓ Yes
Same proxy IP for solve and browse? → No → Pin proxy IP (sticky session)
    ↓ Yes
Cookie set on correct domain (.example.com)? → No → Fix domain
    ↓ Yes
Cookie less than 15 minutes old? → No → Re-solve the challenge
    ↓ Yes
TLS fingerprint matches a browser? → No → Use curl_cffi or tls-client
    ↓ Yes
Site may have additional bot detection → Use headless browser instead

Usa la misma configuración de navegador en local, staging y CI

En equipos distribuidos — por ejemplo, una agencia en Ciudad de México con los runners de CI en Europa — se repite el mismo patrón: el test pasa en local y falla en el pipeline, casi siempre por el viewport, el idioma o el User-Agent por defecto. Centraliza la construcción del driver:

from selenium import webdriver

def make_driver(headless: bool = True) -> webdriver.Chrome:
    options = webdriver.ChromeOptions()
    if headless:
        options.add_argument('--headless=new')
    options.add_argument('--window-size=1280,800')
    options.add_argument('--lang=es-ES')
    return webdriver.Chrome(options=options)

Con viewport, idioma y user-agent idénticos, los resultados vuelven a ser comparables.


Cómo encaja CaptchaAI en tu pipeline

El patrón es el mismo en cualquier lenguaje o framework de pruebas: tu test detecta el widget en una página que tú controlas, envía a CaptchaAI los datos públicos (sitekey, URL, tipo de CAPTCHA), recibe el token, lo inyecta en el formulario y deja que tu backend lo verifique con el proveedor.

Como CaptchaAI factura por thread — el plan BASIC cuesta $15/mes con 5 threads y resoluciones ilimitadas —, volver a resolver de forma preventiva no dispara la factura con cada intento: lo que limita es la concurrencia, no el número de resoluciones.


Qué medir para detectar el bucle antes que tus usuarios

Instrumenta dos métricas: reintentos de challenge por sesión — si sube, algo se desincronizó — y tiempo de resolución por intento. Conserva las trazas (logs, capturas, HAR): con fallos intermitentes son lo único que permite reproducir el incidente.


Buenas prácticas en tu entorno de QA

  • Prueba sobre tu propia aplicación o sobre entornos autorizados, y respeta los términos de servicio y la normativa de protección de datos aplicable.
  • Mantén una API key separada para QA y no mezcles métricas con producción.
  • Vigila los cambios de tu proveedor: los portales de trámites y los marketplaces regionales ajustan sus reglas de Cloudflare con frecuencia.

Preguntas frecuentes

¿Por qué el bucle solo aparece en CI y no en mi máquina local?

Porque el runner sale a internet por otra IP y arranca el navegador con otro viewport e idioma. Iguala la salida de red y la configuración del driver y el bucle desaparece.

¿Cambiar de IP sirve para romper el bucle?

No, es contraproducente: cambiar de IP invalida la cookie que ya tenías. Lo que necesitas es mantener la misma IP durante toda su vida útil.

¿El bucle significa que bloquearon mi API key?

No. La API key solo autentica tu cuenta ante CaptchaAI y el sitio de destino nunca la ve: un bucle indica desajuste de sesión, no bloqueo de cuenta.

¿Cuántos threads necesito si tengo que volver a resolver a menudo?

Depende de cuántas sesiones mantengas en paralelo, no de cuántas veces resuelvas: cada thread atiende una resolución en curso.


Rompe el bucle del Cloudflare Challenge con CaptchaAI

Sincroniza IP, User-Agent y cookie, y deja la resolución en manos de la API: captchaai.com.


Guías relacionadas

Valida tus integraciones CAPTCHA en entornos propios con CaptchaAI.

Los comentarios están deshabilitados para este artículo.