Cloudflare "Un momento" en Python: Por qué requests falla
La página 'Un momento...' de Cloudflare es un desafío de JavaScript, y Python requests no puede ejecutar JavaScript. Aquí te mostramos cómo detectarlo, por qué afecta más a los servidores que a las laptops, por qué los trucos al estilo cloudscraper dejaron de ser suficientes y qué logró pasar en nuestra prueba del 29 de septiembre de 2026.
'Un momento...' es la página de desafío de Cloudflare. Solicita al cliente ejecutar JavaScript y demostrar que es un navegador antes de que el sitio responda. Python requests no puede ejecutar JavaScript, y desde un servidor de centro de datos su IP y el apretón de manos TLS ya parecen automatizados. Lo que pasa es un navegador real en una IP limpia, o un desbloqueador web que ejecute uno.
Esta guía muestra cómo detectar el desafío en Python, por qué el mismo script pasa en tu laptop y falla en tu servidor, por qué los trucos de la era cloudscraper dejaron de ser suficientes y qué realmente logró pasar cuando lo probamos el 29 de septiembre de 2026. Eso incluye el sitio donde nada de lo que intentamos pasó de 'Un momento'.
- Es un desafío, no un error. Cloudflare marca las respuestas desafiadas con el encabezado
cf-mitigated: challenge. En nuestra prueba llegaron como HTTP 403. - Los encabezados no lo solucionarán. 'Enable JavaScript and cookies to continue' significa que la página quiere un motor de JavaScript. Ninguna cadena User-Agent lo proporciona.
- Servidor versus laptop es principalmente reputación de IP más la huella digital TLS. Una IP residencial por sí sola no pasó Indeed o Glassdoor en nuestra prueba.
- Lo que pasó: el Web Unlocker logró Glassdoor (después de un navegador sin cabeza, 17.2 s) e Indeed (primer intento, 2.6 s). StockX aún devolvió 'Un momento...' y se reportó como
js_challenge. - Fija una sesión. Reutilizar la autorización del navegador redujo una segunda solicitud a Glassdoor de 14.0 s a 1.4 s en nuestra prueba.
Qué es realmente la página "Un momento..."
Cloudflare la llama una Página de Desafío intersticial: una puerta de página completa entre el visitante y el sitio mientras Cloudflare decide si el visitante es automatizado. Su documentación sobre páginas de desafío describe tres tipos. Un desafío no interactivo solo necesita que el navegador ejecute el JavaScript inyectado. Un desafío gestionado permite a Cloudflare elegir el tipo de desafío a partir de las señales de la solicitud. Un desafío interactivo pide al visitante que haga algo. Cuando la verificación pasa, la solicitud original continúa al sitio y el navegador obtiene una cookie cf_clearance que le permite saltarse la puerta por un tiempo.
Un cliente HTTP de Python nunca llega tan lejos. Descarga la página de desafío, no tiene motor para ejecutar el script y te entrega el HTML de la puerta en lugar de la página que solicitaste.
Por qué el desafío regresa como un 403, y cómo detectarlo
En nuestra prueba, cada página de desafío llegó con el estado 403, por lo que raise_for_status() lanza 403 Client Error: Forbidden y la gente busca 'cloudflare un momento 403'. El estado por sí solo no puede distinguir un desafío de cualquier otra negativa. La señal confiable es un encabezado: Cloudflare documenta que una respuesta desafiada lleva cf-mitigated: challenge, sea cual sea el recurso que solicitaste. Verifica eso primero, luego el título como respaldo:
import re
import requests
URL = "https://www.glassdoor.com/Reviews/Google-Reviews-E9079.htm"
HEADERS = {
"User-Agent": (
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
"(KHTML, like Gecko) Chrome/150.0.0.0 Safari/537.36"
),
"Accept-Language": "en-US,en;q=0.9",
}
def title_of(html: str) -> str:
m = re.search(r"<title[^>]*>(.*?)</title>", html, re.I | re.S)
return m.group(1).strip() if m else ""
def is_cloudflare_challenge(r: requests.Response) -> bool:
if r.headers.get("cf-mitigated") == "challenge": # documented by Cloudflare
return True
if "just a moment" in title_of(r.text).lower():
return True
return "enable javascript and cookies to continue" in r.text[:20000].lower()
r = requests.get(URL, headers=HEADERS, timeout=30)
print(r.status_code, is_cloudflare_challenge(r), title_of(r.text))
# 29 Sep 2026, plain request through a residential proxy (US exit):
# 403, body asking to 'Enable JavaScript and cookies to continue'
No reintentes una solicitud desafiada en un bucle cerrado con el mismo cliente. Cada reintento obtiene la misma puerta, y algunos sitios responden a fallas repetidas con reglas más estrictas. Así es como los scrapers terminan atrapados en un bucle de 'Un momento'.
"Enable JavaScript and cookies to continue" y "tu navegador no soporta JavaScript"
Estas cadenas son el texto de respaldo de una página de desafío, mostrado a clientes que no ejecutan su script. Cuando Python requests te muestra uno de ellos, el sitio no se está quejando de tus encabezados. Te está diciendo, literalmente, que necesita un motor de JavaScript y un contenedor de cookies que mantenga lo que el script establece.
Eso es exactamente lo que nuestra ejecución residencial simple recibió de Indeed y Glassdoor: un 403 con 'Enable JavaScript and cookies to continue', desde una IP doméstica limpia. Agregar un User-Agent de Chrome, un encabezado Accept-Language o un Referer no cambia nada, porque la página no los está leyendo para decidir. Está esperando que el script se ejecute.
Por qué tu scraper funciona localmente pero es bloqueado por Cloudflare en un servidor
Esta es la versión más común de la queja: el script funciona en una laptop, luego recibe 'Un momento' tan pronto como se ejecuta en una VM en la nube. Dos cosas cambian entre esas máquinas.
- La IP. En tu laptop el script sale desde una dirección de ISP doméstico. En una VM sale desde un rango de proveedor de alojamiento, que las defensas contra bots puntúan como probablemente automatizado antes de leer cualquier otra cosa. Nuestro servidor de centro de datos en Frankfurt fue rechazado por Glassdoor, Indeed, Zillow y Leboncoin, entre otros.
- La huella digital TLS. El apretón de manos de Python parece Python en ambas máquinas, pero en una IP doméstica un sitio puede tolerarlo y en una IP de alojamiento rara vez lo hará. Nuestro explicador sobre curl_cffi vs requests muestra cuánto del apretón de manos controla una biblioteca.
La columna residencial de nuestra prueba muestra el límite de corregir solo la IP. Zillow y Leboncoin sirvieron la página real a una solicitud simple a través de un proxy residencial. Indeed y Glassdoor aún respondieron con el desafío de JavaScript. Tu laptop puede chocar con la misma pared en un sitio más estricto. Simplemente lo hace con menos frecuencia.

Por qué los trucos de la era cloudscraper dejan de funcionar
cloudscraper fue construido para la antigua página 'I'm Under Attack' de Cloudflare. Esa página principalmente verificaba si el cliente podía ejecutar un cálculo de JavaScript, por lo que la biblioteca obtenía el desafío, resolvía el script en un intérprete incrustado, enviaba la respuesta de vuelta y mantenía la cookie, todo sobre requests. Durante mucho tiempo eso fue suficiente, y es la razón por la que 'alternativa a cloudscraper' es ahora una búsqueda que la gente hace.
Varias cosas han cambiado desde entonces, y no dependen de ninguna versión de biblioteca en particular:
- El apretón de manos aún dice Python. Una biblioteca construida sobre requests se conecta a través de la pila TLS de Python, por lo que el ClientHello no coincide con el navegador que su User-Agent afirma ser. Esa discrepancia es visible antes de que cualquier JavaScript se ejecute.
- El desafío ya no es solo aritmética. Los desafíos gestionados eligen la verificación a partir de las señales de la solicitud, y las detecciones de JavaScript de Cloudflare leen el entorno del navegador. Un intérprete que evalúa un script no es un navegador que renderiza una página.
- La autorización está vinculada al cliente. La documentación de autorización de Cloudflare dice que una cookie cf_clearance está vinculada al visitante y dispositivo al que se emitió, para prevenir su reutilización entre máquinas. Copiar una de tu navegador a un script en otra máquina está diseñado para fallar.
- La IP sigue siendo tuya. Resolver la página no cambia una IP de rango de alojamiento, y algunos sitios rechazan esas antes de que se sirva cualquier desafío.
A partir del 29 de septiembre de 2026, el último push del repositorio de cloudscraper fue en junio de 2025. Eso no es un veredicto sobre ningún sitio en particular, y no lo evaluamos para este artículo. Significa que deberías probarlo contra tus propios objetivos en lugar de asumir que funciona. Las alternativas solucionan cada una parte del problema. curl_cffi reproduce un apretón de manos TLS de navegador pero no ejecuta JavaScript. Playwright u otro navegador real ejecuta el desafío pero aún necesita una IP limpia, y es pesado para operar a gran escala. El desbloqueador combina los dos y te dice cuándo ninguno funcionó.
Qué pasa: la ruta del desbloqueador web
El Web Unlocker de QuantumProxies.io maneja un GET en etapas. Primero, hasta tres intentos TLS, cada uno en una salida residencial fresca bajo una huella digital de navegador diferente. Si cada intento regresa desafiado, escala a un navegador sin cabeza que ejecuta el desafío. Si la página sigue bloqueada después de eso, obtienes un estado de error con x-qp-unlocker-blocked y una clase de bloqueo, nunca una página de desafío disfrazada de 200. Desde Python es una configuración de proxy:
import os
import time
import requests
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
user = os.environ["QP_UNLOCK_USER"] + "-country-us" # unlock-XXXXXXXX-country-us
PROXY = f"http://{user}:{os.environ['QP_UNLOCK_PASSWORD']}@unlock.quantumproxies.io:9000"
s = requests.Session()
s.proxies = {"http": PROXY, "https": PROXY}
s.verify = False # or the unlocker CA file from /api/v1/scraper/unlock/ca
s.headers["x-qp-session"] = "glassdoor-01" # same value on every call: same exit, reusable clearance
for attempt in (1, 2): # same page twice here; in practice, the next page
t0 = time.monotonic()
r = s.get(URL, timeout=120)
print(attempt, r.status_code, f"{time.monotonic() - t0:.1f}s",
"rendered:", r.headers.get("x-qp-unlocker-rendered"),
"clearance:", r.headers.get("x-qp-clearance"),
"block:", r.headers.get("x-qp-block-class"))
Los encabezados x-qp-* son leídos por el desbloqueador y nunca se reenvían al sitio. Si prefieres no lidiar con el certificado en absoluto, el endpoint REST POST https://app.quantumproxies.io/api/v1/scraper/unlock toma la URL en un cuerpo JSON con tu clave API y devuelve blocked, blockClass y vendor en la respuesta. Nuestro tutorial de la API del desbloqueador web recorre ambos.
Medido: dos muros al estilo Cloudflare que pasaron, uno que no
Medido el 29 de septiembre de 2026. Las solicitudes simples fueron curl con un User-Agent de Chrome, desde un servidor de centro de datos en Frankfurt y a través de un proxy residencial con una salida en EE.UU. El desbloqueador utilizó el nivel residencial con una salida en EE.UU. Los tiempos son de reloj de pared para una solicitud.
| Sitio | Simple, servidor de centro de datos | Simple, proxy residencial | Web Unlocker |
|---|---|---|---|
| Glassdoor (reseñas de empresas) | 403, página de 'Seguridad' | 403, 'Enable JavaScript and cookies to continue' | 200, 2 intentos + navegador sin cabeza, 17.2 s |
| Indeed (búsqueda de empleo) | 403, página de 'Verificación de Seguridad' | 403, 'Enable JavaScript and cookies to continue' | 200, 1 intento, sin navegador, 2.6 s |
| StockX (página de producto) | 403 | 403 | 403 'Un momento...', x-qp-block-class js_challenge, vendor cloudflare, 47.8 s |
| DoorDash (página de ciudad) | 403 | 403 | 403, x-qp-block-class ip_reputation, vendor cloudflare |
Glassdoor es el camino completo. Dos intentos TLS fueron desafiados, luego el navegador sin cabeza ejecutó el desafío y devolvió la página de reseñas. Indeed es el interesante: el desafío que la solicitud residencial simple recibió nunca apareció para el primer intento del desbloqueador. Una huella digital TLS de navegador en una salida residencial fresca fue suficiente, sin un navegador.
StockX es el fracaso honesto. El desbloqueador escaló, gastó 47.8 segundos, y la página seguía siendo 'Un momento...'. Regresó como un 403 con x-qp-block-class: js_challenge y x-qp-vendor: cloudflare, no como un 200 que tu analizador habría intentado leer. DoorDash, también detrás de Cloudflare, fue rechazado en la IP (ip_reputation) en lugar de desafiado. Algunas configuraciones de Cloudflare no se abren para ninguna de las herramientas en este artículo, y es mejor saberlo por un encabezado que por un conjunto de datos vacío.
Supera los desafíos de JavaScript con el Web Unlocker
Reutilizando la autorización: por qué la segunda solicitud es rápida
La escalada del navegador es la parte lenta, y no quieres pagar por ella en cada página. Cuando el navegador sin cabeza pasa un desafío, gana una autorización. Envía el mismo valor x-qp-session, o mantén el mismo -session-<id> en el nombre de usuario del proxy, en las siguientes solicitudes y salen desde la misma salida, reutilizando esa autorización en lugar de ejecutar el navegador nuevamente. El encabezado x-qp-clearance te dice qué pasó en cada llamada: minted cuando se ganó una nueva autorización, hit cuando se reutilizó una almacenada, miss cuando no había nada para reutilizar.
En una prueba de Glassdoor el 29 de septiembre de 2026, con la sesión fijada en el nombre de usuario del proxy y dos conexiones separadas, la primera solicitud pasó por el navegador en 14.0 segundos (minted) y la segunda regresó en 1.4 segundos (hit). La fijación importa debido a cómo Cloudflare vincula la autorización: está ligada al visitante y dispositivo que la ganó, y permanece válida durante el tiempo que el sitio configura a menos que Cloudflare decida que la sesión parece sospechosa. Cambia la salida o la huella digital y eres un nuevo visitante nuevamente.

Cuando las solicitudes simples aún funcionan en un sitio de Cloudflare
Muchos sitios funcionan detrás de Cloudflare solo como un CDN, o con reglas de bots que permiten el tráfico ordinario, y las solicitudes simples funcionan en ellos. Estar 'en Cloudflare' no es una razón para recurrir a un navegador. La señal es la respuesta. Si no hay un encabezado cf-mitigated: challenge, ningún título 'Un momento' y ningún 403, mantén la solicitud simple, y escala solo las URL que realmente son desafiadas. El ayudante de primero simple en nuestro tutorial hace exactamente eso.
Dos problemas vecinos tienen sus propias guías. Una página que dice 'Error 1020 Acceso denegado' es una regla de firewall que te rechaza, no un desafío, y ninguna cantidad de JavaScript lo soluciona; consulta qué desencadena el error 1020 de Cloudflare y qué lo soluciona. Para la imagen más amplia de las defensas de Cloudflare más allá de Python, lee cómo superar los desafíos de Cloudflare en 2026. Si la página detrás del muro es Glassdoor, nuestra guía para extraer salarios y reseñas de Glassdoor cubre el lado de los datos.
Preguntas frecuentes
¿Por qué mi scraper funciona localmente pero es bloqueado por Cloudflare en la nube?
Porque la IP cambia. En casa tu script sale desde una dirección de ISP residencial; en una VM en la nube sale desde un rango de alojamiento que las defensas contra bots desconfían a primera vista. El apretón de manos TLS de Python es el mismo en ambos, y en un sitio más estricto se desafía incluso desde casa. En nuestra prueba, una IP residencial por sí sola no pasó Indeed o Glassdoor.
¿Por qué la página "Un momento" de Cloudflare devuelve un 403?
La página de desafío se sirve en lugar de la página que solicitaste, y en nuestra prueba vino con el estado 403, por lo que requests lanza '403 Client Error: Forbidden'. El estado no es específico de los desafíos. Verifica el encabezado de respuesta cf-mitigated, que Cloudflare documenta como configurado en challenge en cada respuesta de Página de Desafío, o el título 'Un momento'.
¿cloudscraper sigue funcionando, y cuál es una buena alternativa?
Puede que aún funcione en sitios con configuraciones indulgentes, así que pruébalo contra tus objetivos. Funciona sobre la pila TLS de Python y resuelve scripts en un intérprete, mientras que los desafíos actuales también miran el apretón de manos y el entorno del navegador. Alternativas: curl_cffi para un apretón de manos de navegador, un navegador real para el JavaScript, o un desbloqueador web que hace ambos.
¿Puede Python requests pasar un desafío de Cloudflare por sí solo?
No. requests no ejecuta JavaScript, por lo que no puede completar un desafío que depende de ejecutar el script de la página. Puede obtener páginas de sitios de Cloudflare que no te estén desafiando. Una vez que aparece cf-mitigated: challenge, necesitas un navegador real, directamente o a través de un servicio que ejecute uno, y preferiblemente una IP residencial limpia.
¿Puede el desbloqueador web resolver Cloudflare Turnstile u otros captchas?
Los desafíos de JavaScript no interactivos son respondidos por la escalada del navegador sin cabeza. Los desafíos interactivos que necesitan una acción humana, como una casilla de verificación, deslizador, presionar y mantener o cuadrícula de imágenes, no se resuelven. La solicitud falla en lugar de devolver el desafío como un 200, y el endpoint REST lo etiqueta con la clase de bloqueo captcha y el proveedor, para que tu código pueda decidir si omitir la URL, ralentizar o intentar más tarde.
¿Cuánto dura una autorización de Cloudflare?
Tanto como el sitio lo configure. Cloudflare llama a la configuración Challenge Passage, y la autorización puede terminar temprano si Cloudflare decide que la sesión parece sospechosa. La cookie cf_clearance está vinculada al visitante y dispositivo que la ganó, por lo que solo ayuda si las solicitudes posteriores mantienen la misma salida y huella digital, que es lo que hace fijar x-qp-session.
La versión corta: 'Un momento...' pide un navegador, por lo que ningún truco de encabezado en requests lo satisfará. Detéctalo con cf-mitigated, mantén las solicitudes simples para todo lo que no sea desafiado, envía las URL desafiadas a través de algo que ejecute un navegador real desde una IP limpia, y fija una sesión para que pagues por ese navegador una vez en lugar de en cada página.