Resolviste el CAPTCHA de Turnstile y obtuviste un token válido, pero el sitio de destino sigue devolviendo 403 Forbidden. Esta guía cubre todas las causas.
¿Por qué 403 después del token válido?
| Causa | Probabilidad |
|---|---|
| Falta la cookie qa_validation_cookie | Muy común |
| El token expiró | Común |
| Endpoint de envío incorrecto | Común |
| Faltan cabeceras de solicitud | Moderado |
| IP no coincide entre resolver y enviar | Moderado |
| Cloudflare Challenge (no Turnstile) | A veces confundido |
Causa 1: Falta la cookie qa_validation_cookie
Turnstile establece cookies durante la validación. Si no incluyes estas cookies en la solicitud posterior, Cloudflare la bloquea.
import requests
session = requests.Session()
# Step 1: Load the page to get initial cookies
session.get("https://example.com")
# Step 2: Solve Turnstile
token = solve_turnstile(
api_key="YOUR_API_KEY",
sitekey="TURNSTILE_SITEKEY",
pageurl="https://example.com",
)
# Step 3: Submit token to the validation endpoint
# This sets qa_validation_cookie cookie
resp = session.post("https://example.com/api/verify", data={
"cf-turnstile-response": token,
}, headers={
"Content-Type": "application/x-www-form-urlencoded",
"Origin": "https://example.com",
"Referer": "https://example.com/",
})
# Step 4: Now make your actual request WITH the session cookies
resp = session.get("https://example.com/protected-page")
print(resp.status_code) # Should be 200 now
Causa 2: Token caducado
Los tokens de Turnstile duran aproximadamente 300 segundos, pero úsalos inmediatamente para obtener mejores resultados.
import time
# Solve
start = time.time()
token = solve_turnstile(...)
solve_time = time.time() - start
# Check if token is still fresh
if solve_time > 240: # > 4 minutes is risky
print("Token may be too old, solving again...")
token = solve_turnstile(...)
# Submit immediately
submit_token(token)
Causa 3: método de envío incorrecto
Descubra cómo el sitio envía el token Turnstile:
# Some sites use a hidden form field
data = {
"cf-turnstile-response": token,
"username": "user",
"password": "pass",
}
# Some sites use a custom header
headers = {
"X-Turnstile-Token": token,
}
# Some sites use JSON body
json_data = {
"turnstileToken": token,
"email": "user@example.com",
}
Cómo encontrar el nombre de campo correcto:
- Abra la pestaña Red DevTools → del navegador
- Completa el desafío Turnstile manualmente
- Encuentre la solicitud de envío del formulario
- Mire el cuerpo de la solicitud para ver el nombre del campo del token.
Causa 4: Faltan cabeceras
Cloudflare verifica la coherencia de las cabeceras de solicitud:
headers = {
"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",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Accept-Language": "en-US,en;q=0.9",
"Origin": "https://example.com",
"Referer": "https://staging.example.com/qa-login",
"Sec-Fetch-Dest": "document",
"Sec-Fetch-Mode": "navigate",
"Sec-Fetch-Site": "same-origin",
}
session.headers.update(headers)
Causa 5: Cloudflare Challenge vs Turnstile
Turnstile y Cloudflare Challenge son sistemas diferentes:
| Característica | Turnstile | Cloudflare Challenge |
|---|---|---|
| Apariencia | Casilla de verificación visible en la página | Pantalla de challenge de página completa |
| Método CaptchaAI | turnstile |
cloudflare_challenge |
| Campo de token | cf-turnstile-response |
N/A (basado en cookies) |
Si ves un challenge de página completa, usa method=cloudflare_challenge en su lugar.
Ejemplo de trabajo completo
import requests
import time
import re
def solve_turnstile_and_access(target_url, api_key):
"""Complete flow: solve Turnstile and access protected page."""
session = requests.Session()
session.headers.update({
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
})
# Load page, get cookies and sitekey
resp = session.get(target_url)
match = re.search(r'data-sitekey="([^"]+)"', resp.text)
if not match:
raise RuntimeError("Turnstile sitekey not found")
sitekey = match.group(1)
# Solve via CaptchaAI
submit_resp = requests.post("https://ocr.captchaai.com/in.php", data={
"key": api_key,
"method": "turnstile",
"sitekey": sitekey,
"pageurl": target_url,
"json": 1,
}, timeout=30)
task_id = submit_resp.json()["request"]
# Poll
for _ in range(12):
time.sleep(5)
poll = requests.get("https://ocr.captchaai.com/res.php", params={
"key": api_key, "action": "get",
"id": task_id, "json": 1,
}, timeout=15)
data = poll.json()
if data.get("status") == 1:
token = data["request"]
break
else:
raise TimeoutError("Solve timeout")
# Submit token using the same session
form_resp = session.post(target_url, data={
"cf-turnstile-response": token,
}, headers={
"Origin": f"https://{requests.utils.urlparse(target_url).netloc}",
"Referer": target_url,
})
return session, form_resp
# Usage
session, resp = solve_turnstile_and_access(
"https://staging.example.com/qa-login",
"YOUR_API_KEY",
)
# session now has valid cookies for subsequent requests
Solución de problemas
| Problema | Causa | Solución |
|---|---|---|
| 403 a pesar del token válido | Faltan cookies de sesión | Usa la misma sesión para todas las solicitudes |
| 403 en páginas siguientes | qa_validation_cookie no establecida | La validación del token debe devolver la cookie |
| Funciona una vez, luego 403 | Cookie caducada | Vuelve a resolver para obtener una cookie fresca |
| Siempre 403 | Challenge de página completa, no Turnstile | Usa el método cloudflare_challenge |
Preguntas frecuentes
¿Cuánto dura qa_validation_cookie?
Normalmente de 30 minutos a 24 horas. Si las solicitudes posteriores empiezan a fallar, vuelve a resolver Turnstile.
¿Necesito un proxy para Turnstile?
Generalmente no: la tasa de éxito del 100% de CaptchaAI en Turnstile suele funcionar sin proxies. Añade un proxy solo si el sitio verifica la coherencia de IP.
¿Puedo pasar qa_validation_cookie a otra sesión?
Sí, pero está vinculada al User-Agent y puede estar vinculada a la IP. Mantén ambos consistentes.
Guías relacionadas
- Diagnóstico de caída de la tasa de éxito
- Cómo resolver Cloudflare Turnstile usando API
Corrige errores 403 con CaptchaAI.