¿Recibes un 403 en un sitio con Cloudflare y no sabes si es cosa de tus encabezados o de un CAPTCHA? El código HTTP te lo dice: un 403 viene del Browser Integrity Check (BIC) y se corrige ajustando los encabezados; un 503 es un Challenge CAPTCHA que hay que resolver. Son dos capas independientes, y confundirlas hace perder horas.
Diferencias entre BIC y Challenge CAPTCHA
Esta tabla resume por qué cada bloqueo necesita una estrategia distinta:
| Característica | Browser Integrity Check | Challenge CAPTCHA |
|---|---|---|
| Qué comprueba | Solo cabeceras HTTP | Comportamiento del navegador + entorno |
| ¿Se requiere JavaScript? | No (nivel de cabecera) | Sí |
| El usuario ve | Nada (pasa) o 403 (falla) | Widget de challenge o página JS |
| Estado HTTP en error | 403 | 503 |
| ¿Tiene solución? | No: corrige las cabeceras o se bloquea | Sí, resuelve CAPTCHA |
| Cookie emitida | No | qa_validation_cookie |
| ¿Se necesita CaptchaAI? | No: corregir cabeceras de solicitud | Sí |
| Habilitado por defecto | Sí (todos los planes de Cloudflare) | No (configurado por regla) |
| Aplica a | Cada solicitud | Solo reglas WAF coincidentes |
El código HTTP te orienta al instante:
- 403 con encabezados mínimos y 200 con los de navegador: solo BIC.
- 503: un Challenge lanzado por una regla WAF.
- 403 con cualquier combinación: bloqueo de IP.
El BIC actúa en todas las solicitudes; el Challenge, solo cuando coincide una regla WAF. Por eso un script estable puede fallar de golpe sin que hayas tocado nada.
Browser Integrity Check (BIC): la primera capa
El BIC revisa los encabezados de cada solicitud para descartar tráfico que no parece de un navegador. Corre antes que cualquier reto de JavaScript, así que es lo primero que debes superar.
Qué señales revisa el BIC
| Comprobación | Qué busca | Fallos habituales |
|---|---|---|
| Validación del User-Agent | UA ausente, vacío o defectuoso | python-requests/2.31, curl, wget |
| Orden de los encabezados | Encabezados en orden inusual | Bibliotecas HTTP sin navegador |
| Encabezados de navegador ausentes | Faltan Accept, Accept-Language, Accept-Encoding |
Clientes HTTP mínimos |
| Firmas de bots conocidos | UA de spammers o de escáneres | Googlebot desde IP que no son de Google |
| IP en listas de abuso | IP maliciosas conocidas | IP de centros de datos en listas de bloqueo |
Cómo decide el BIC
Request hits Cloudflare edge
↓
BIC evaluates HTTP headers:
├─ User-Agent present and valid?
├─ Required headers (Accept, Accept-Language) present?
├─ Header order consistent with browser?
└─ IP not on abuse list?
↓
Pass → Request forwarded to origin
OR
Fail → 403 Forbidden (no challenge offered)
Pasar el BIC con los encabezados correctos
El BIC se supera con encabezados coherentes con los de un navegador real; no interviene ningún CAPTCHA:
import requests
# ❌ FAILS BIC — minimal headers
response = requests.get("https://cloudflare-protected.com")
# User-Agent: python-requests/2.31.0
# Result: 403 Forbidden
# ✅ PASSES BIC — browser-like headers
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,"
"image/avif,image/webp,*/*;q=0.8",
"Accept-Language": "en-US,en;q=0.9",
"Accept-Encoding": "gzip, deflate, br",
"Connection": "keep-alive",
"Upgrade-Insecure-Requests": "1",
"Sec-Fetch-Dest": "document",
"Sec-Fetch-Mode": "navigate",
"Sec-Fetch-Site": "none",
"Sec-Fetch-User": "?1",
}
response = requests.get("https://cloudflare-protected.com", headers=headers)
# Result: 200 OK (passed BIC)
El mismo enfoque en Node.js
const axios = require("axios");
// Browser-like headers to pass BIC
const headers = {
"User-Agent":
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/120.0.0.0",
Accept:
"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,*/*;q=0.8",
"Accept-Language": "en-US,en;q=0.9",
"Accept-Encoding": "gzip, deflate, br",
Connection: "keep-alive",
"Upgrade-Insecure-Requests": "1",
"Sec-Fetch-Dest": "document",
"Sec-Fetch-Mode": "navigate",
"Sec-Fetch-Site": "none",
"Sec-Fetch-User": "?1",
};
async function fetchWithBIC(url) {
const response = await axios.get(url, { headers });
console.log(`Status: ${response.status}`);
return response.data;
}
Los Challenge CAPTCHA de Cloudflare
Los Challenge se activan una vez superado el BIC. A diferencia de este, exigen ejecutar JavaScript y presentan un reto con solución.
Tipos de Challenge de Cloudflare
| Tipo de desafío | Cuándo se activa | ¿Visible para el usuario? | Método CaptchaAI |
|---|---|---|---|
| Widget de Turnstile | Incrustado en la página por el desarrollador | Widget en el formulario | turnstile |
| Managed Challenge | Coincidencia de reglas WAF | Adaptativo (invisible/checkbox) | turnstile |
| JavaScript Challenge | Regla IUAM o WAF | Página "Comprobando tu navegador" | cloudflare_challenge |
| Interactive Challenge | Alta sospecha | Desafío a página completa | cloudflare_challenge |
Cómo fluye un Challenge
Request passes BIC check
↓
WAF rules evaluate bot score, IP, path
↓
Rule triggers CAPTCHA challenge (HTTP 503)
↓
Browser executes JavaScript challenge
↓
Challenge solved → qa_validation_cookie cookie issued
↓
Subsequent requests pass with cookie
Cómo saber si un bloqueo es BIC o Challenge
Para saber qué te bloquea, lanza dos solicitudes y compara los códigos:
- Una con encabezados mínimos, para provocar el BIC.
- Otra con encabezados de navegador, para superarlo.
import requests
def diagnose_cloudflare_block(url):
"""Determine if block is BIC or CAPTCHA."""
# Intentionally use minimal headers to trigger BIC
minimal_response = requests.get(url, timeout=15, allow_redirects=False)
# Use browser-like headers to satisfy BIC checks
browser_headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 Chrome/120.0.0.0",
"Accept": "text/html,application/xhtml+xml,*/*;q=0.8",
"Accept-Language": "en-US,en;q=0.9",
"Accept-Encoding": "gzip, deflate, br",
}
browser_response = requests.get(
url, headers=browser_headers, timeout=15, allow_redirects=False
)
result = {
"minimal_headers_status": minimal_response.status_code,
"browser_headers_status": browser_response.status_code,
}
if minimal_response.status_code == 403 and browser_response.status_code == 200:
result["diagnosis"] = "BIC only — fix headers to pass"
result["fix"] = "Add browser-like headers"
elif minimal_response.status_code == 403 and browser_response.status_code == 403:
result["diagnosis"] = "IP or WAF block — not just BIC"
result["fix"] = "Change IP or check WAF rules"
elif browser_response.status_code == 503:
html = browser_response.text
if "jschl" in html:
result["diagnosis"] = "IUAM JavaScript challenge"
result["fix"] = "Use CaptchaAI cloudflare_challenge method"
elif "cf-turnstile" in html or "challenge-platform" in html:
result["diagnosis"] = "Managed/Turnstile challenge"
result["fix"] = "Use CaptchaAI turnstile method"
else:
result["diagnosis"] = "Unknown Cloudflare challenge"
result["fix"] = "Inspect page source for challenge type"
elif browser_response.status_code == 200:
html = browser_response.text
if "cf-turnstile" in html:
result["diagnosis"] = "Page loads but has Turnstile widget"
result["fix"] = "Use CaptchaAI turnstile method for form submission"
else:
result["diagnosis"] = "No challenge — page accessible"
result["fix"] = "None needed"
return result
# Usage
diagnosis = diagnose_cloudflare_block("https://example-cf-site.com")
print(f"Diagnosis: {diagnosis['diagnosis']}")
print(f"Fix: {diagnosis['fix']}")
Diagnóstico rápido de errores
Antes de los casos completos, esta tabla cubre los síntomas frecuentes:
| Síntoma | Causa | Solución |
|---|---|---|
| 403 con encabezados mínimos, 200 con encabezados de navegador | Solo BIC | Usa encabezados similares a los de un navegador |
| 403 con cualquier combinación de encabezados | IP bloqueada o regla WAF | Prueba con otra IP o salida de red autorizada |
| 503 con página de desafío | Challenge CAPTCHA (no BIC) | Usa CaptchaAI |
| 403 de forma intermitente | Límite de solicitudes o basado en sesión | Baja la frecuencia y mantén la sesión |
| Encabezados correctos pero sigue el 403 | Comprobación de huella TLS (JA3) | Usa curl_cffi o un navegador real |
Cuatro situaciones típicas
Cuatro patrones habituales y qué hacer en cada uno.
Caso 1 — solo bloqueo BIC. Basta con encabezados de navegador:
Request with python-requests UA → 403
Request with Chrome UA → 200 ✓
Fix: Add proper browser headers. No CaptchaAI needed.
Caso 2 — BIC superado y luego CAPTCHA. Encabezados correctos, pero salta el reto:
Request with Chrome UA → 503 (challenge page)
Fix: Headers are fine. Usa CaptchaAI para solve the challenge.
Caso 3 — BIC y CAPTCHA a la vez. Las dos cosas, en orden:
Request with python-requests UA → 403 (BIC block)
Request with Chrome UA → 503 (CAPTCHA challenge)
Fix: First fix headers, then solve CAPTCHA con CaptchaAI.
Caso 4 — bloqueo de IP. No hay reto que resolver:
Request with python-requests UA → 403
Request with Chrome UA → 403
Fix: Neither BIC nor solvable challenge. IP is blocked.
Try different IP/proxy.
Un ejemplo cercano: una agencia que monitorea precios en un marketplace regional protegido por Cloudflare (tipo MercadoLibre o Amazon.es). Al principio recibe 403 en todo — es puro BIC — y lo resuelve con encabezados de navegador. Semanas después aparecen 503 en algunas rutas: se activó un Challenge por reglas WAF, y ahí entra CaptchaAI. Respeta siempre los términos de servicio y la normativa de protección de datos aplicable (GDPR/LOPDGDD, LFPDPPP).
Encabezados recomendados para el BIC
Estos encabezados dejan tu solicitud coherente con un navegador real:
CLOUDFLARE_SAFE_HEADERS = {
# Mandatory
"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",
"Accept-Encoding": "gzip, deflate, br",
# Strongly recommended
"Connection": "keep-alive",
"Upgrade-Insecure-Requests": "1",
# Sec-Fetch headers (modern Chrome)
"Sec-Fetch-Dest": "document",
"Sec-Fetch-Mode": "navigate",
"Sec-Fetch-Site": "none",
"Sec-Fetch-User": "?1",
# Recommended for consistency
"Sec-Ch-Ua": '"Not_A Brand";v="8", "Chromium";v="120", "Google Chrome";v="120"',
"Sec-Ch-Ua-Mobile": "?0",
"Sec-Ch-Ua-Platform": '"Windows"',
}
Y estos disparan el bloqueo:
| Encabezado | Por qué falla |
|---|---|
User-Agent: python-requests/2.31.0 |
Identificador de biblioteca HTTP conocido |
User-Agent: curl/7.81.0 |
Identificador de herramienta de línea de comandos |
Falta el encabezado Accept |
Ningún navegador omite Accept |
Falta Accept-Language |
Todos los navegadores lo envían |
User-Agent: "" (vacío) |
No válido: automatización evidente |
User-Agent: Googlebot/2.1 desde una IP que no es de Google |
Identidad de bot falsa |
Preguntas frecuentes
Dudas habituales sobre ambas capas:
¿Qué diferencia hay entre el 403 del BIC y el 503 de un Challenge?
El código lo dice todo. Un 403 significa que el BIC rechazó tus encabezados y no hay nada que resolver: corrige el User-Agent y las cabeceras Accept. Un 503 es un Challenge que exige ejecutar JavaScript y obtener un token.
¿Cómo distingo un bloqueo de BIC de uno por IP?
Repite la solicitud con encabezados de navegador. Si pasa a 200, era BIC. Si sigue en 403 con encabezados correctos, el problema es la IP o una regla WAF.
Si supero el BIC, ¿aún puede aparecer un CAPTCHA?
Sí. Son capas independientes: pasar el BIC solo significa que tus encabezados parecen legítimos. Aún puedes disparar un CAPTCHA por reglas WAF, puntuación de bot o modo IUAM.
¿CaptchaAI sirve para pasar el BIC?
No. El BIC no presenta un reto resoluble, así que se corrige ajustando los encabezados HTTP. CaptchaAI actúa en la otra capa: resuelve los Challenge de Cloudflare (Turnstile, JavaScript Challenge y Managed Challenge).
¿Cada cuánto conviene actualizar el User-Agent?
Mantenlo alineado con una versión reciente y real de Chrome. Un UA que apunta a una versión retirada es una señal poco natural; refréscalo al cambiar la rama estable que imitas.
Resumen
El BIC bloquea con 403 las solicitudes cuyos encabezados no parecen de un navegador; los Challenge CAPTCHA presentan tareas resolubles con 503. El BIC se arregla enviando encabezados de navegador reales, sin CaptchaAI de por medio; los Challenge sí requieren el método turnstile o el solver de Cloudflare Challenge de CaptchaAI. La regla de oro: diagnostica el bloqueo por su código HTTP, corrige encabezados para el BIC, usa CaptchaAI para los CAPTCHA y cambia de IP en los bloqueos duros.
Artículos relacionados
- Cloudflare Challenge frente a Turnstile: cómo distinguirlos
- GeeTest v3 frente a Cloudflare Turnstile
- Managed Challenge frente a Interactive Challenge en Cloudflare