Quando o Cloudflare dispara a página de desafio (HTTP 503, "Just a moment..."), a automação só segue adiante se um cookie confirmar que o desafio foi resolvido — o que depende de parâmetros como ray, md, cType e cTTimeMs. Reproduzir esse fluxo em staging evita a surpresa clássica de automação que funciona em dev e trava em produção.
Resumo rápido: a página devolve HTTP 503 com um
rayID, roda um proof-of-work em JavaScript e libera o acesso com o cookiecookie_qa_validacao— válido de 15 min a 24 h, preso ao IP e ao User-Agent.
Estrutura da página de desafio (HTTP 503)
Toda página de desafio devolve HTTP 503 com os mesmos elementos: status visível, formulário oculto com os parâmetros e o script que orquestra a verificação:
<!DOCTYPE html>
<html>
<head>
<title>Just a moment...</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
</head>
<body>
<div id="challenge-stage">
<div id="challenge-body-text">
Checking if the site connection is secure
</div>
<div id="challenge-spinner">
<!-- Loading spinner -->
</div>
</div>
<div id="challenge-form" style="display:none">
<form id="challenge-form" action="/..." method="POST">
<!-- Hidden parameters -->
<input type="hidden" name="md" value="...">
<input type="hidden" name="r" value="...">
</form>
</div>
<script src="/cdn-cgi/challenge-platform/h/g/orchestrate/chl_page/v1?ray=...">
</script>
</body>
</html>
Parâmetros do desafio: o que cada campo significa
A Cloudflare expõe esses campos em três lugares: no HTML da página, na URL do script de orquestração e no objeto JavaScript carregado em seguida.
Parâmetros visíveis na página HTML
Identificação:
| Parâmetro | Nome | Para que serve |
|---|---|---|
ray |
Ray ID da Cloudflare | Identifica a requisição e vincula o desafio a ela |
md |
Metadados do desafio | Estado do desafio, criptografado |
r |
Token de resposta | Resposta computada, preenchida pelo JavaScript |
chl_opt |
Opções do desafio | Configuração do script de desafio |
Rastreamento:
| Parâmetro | Nome | Para que serve |
|---|---|---|
cRay |
Ray ID secundário | Rastreia o desafio internamente |
cZone |
Zona do desafio | ID da zona Cloudflare |
cUPMDTk |
Carimbo de tempo | Momento de emissão do desafio |
cHash |
Hash do desafio | Valida a integridade dos dados |
Parâmetros na URL do script de orquestração
O script é carregado a partir de uma URL com estrutura fixa:
/cdn-cgi/challenge-platform/h/g/orchestrate/chl_page/v1?ray=ABC123
Decompondo essa URL:
/cdn-cgi/challenge-platform/— infraestrutura de desafioh/g/— versão/varianteorchestrate/— endpoint de orquestraçãochl_page/v1— versão da páginaray=ABC123— vincula a requisição ao Ray ID
Parâmetros carregados pelo JavaScript do desafio
// Extracted from obfuscated challenge script
window._cf_chl_opt = {
cvId: '2', // Challenge version
cType: 'managed', // Challenge type
cNounce: '...', // Cryptographic nonce
cRay: '...', // Challenge Ray ID
cHash: '...', // Challenge hash
cUPMDTk: '...', // Timestamp
cFPWv: 'g', // sinal de navegador version
cTTimeMs: '4000', // Minimum wait time (ms)
cTplV: 5, // Template version
cLt: '...', // Challenge lifetime
cRq: {}, // Challenge request data
};
Como o token é gerado: do desafio à liberação de acesso
O fluxo completo, requisição por requisição
O Cloudflare troca nove mensagens até liberar o acesso — o mesmo fluxo que a CaptchaAI reproduz pela API:
1. CLIENT → CLOUDFLARE EDGE
GET /protected-page
↓
2. CLOUDFLARE → CLIENT
HTTP 503 + Challenge page HTML
Sets: __cf_bm cookie (bot management tracking)
Contains: ray ID, challenge script URL
↓
3. CLIENT (browser)
Loads challenge script from /cdn-cgi/challenge-platform/...
↓
4. CHALLENGE SCRIPT EXECUTES:
a. Collects browser sinal de navegador:
- Canvas hash
- WebGL renderer
- Screen dimensions
- Installed fonts
- Timezone
- Language
b. Runs proof-of-work:
- Iterates hash computations
- Must find answer matching difficulty
c. Computes timing:
- Enforces minimum wait (cTTimeMs)
- Records actual timing
d. Generates response token:
- Combines sinal de navegador + PoW answer + timing
- Encrypts with challenge nonce
↓
5. CLIENT → CLOUDFLARE
POST /cdn-cgi/challenge-platform/h/g/flow/ov1/...
Body: { r: "encrypted_response", md: "metadata", ... }
↓
6. CLOUDFLARE validates:
- Proof-of-work answer correct?
- Timing within acceptable range?
- sinal de navegador consistent with real browser?
- No replay (nonce check)?
↓
7. CLOUDFLARE → CLIENT
HTTP 200 + Set-Cookie: cookie_qa_validacao=...; path=/; expires=...
+ HTTP redirect to original URL
↓
8. CLIENT → CLOUDFLARE
GET /protected-page
Cookie: cookie_qa_validacao=...
↓
9. CLOUDFLARE → CLIENT
HTTP 200 + Protected content
Linha do tempo: quando cada cookie aparece
A sequência de cookies fica assim:
Request 1: No cookies
→ Challenge page (503)
→ __cf_bm cookie set
Challenge solve:
→ cookie_qa_validacao cookie set
Request 2+: cookie_qa_validacao + __cf_bm
→ Content served (200)
After ~30 mins: cookie_qa_validacao expires
→ Next request triggers new challenge
Cookies do desafio
Quatro cookies aparecem no fluxo; só o de liberação prova que o desafio foi resolvido — os outros cuidam de sessão e roteamento:
| Cookie | Para que serve | Vida útil | Escopo |
|---|---|---|---|
__cf_bm |
Rastreamento de sessão do bot | 30 minutos | Domínio |
cookie_qa_validacao |
Prova de que o desafio foi resolvido | 15 min – 24 horas (configurável) | Domínio |
__cflb |
Afinidade do balanceador de carga | Sessão | Domínio |
_cfuvid |
ID de visitante único | Sessão | Domínio |
A que o cookie cookie_qa_validacao fica preso
O cookie cookie_qa_validacao só é válido dentro do contexto em que foi emitido:
| Fator | Regra |
|---|---|
| Endereço IP | Precisa vir do mesmo IP que resolveu o desafio |
| User-Agent | Precisa bater com o UA usado durante o desafio |
| Domínio | Vale apenas para o domínio que emitiu o cookie |
# ❌ FAILS — IP mismatch
# Solve challenge from IP A, then use cookie_qa_validacao from IP B
# ❌ FAILS — UA mismatch
# Solve with Chrome UA, then send requests with Firefox UA
# ✅ WORKS — Same IP + Same UA
session = requests.Session()
session.headers["User-Agent"] = "Mozilla/5.0 ... Chrome/120.0.0.0"
# Use same session for solving and subsequent requests
Como extrair os parâmetros do desafio via código
As funções abaixo detectam a página de desafio (503) e extraem os parâmetros para debug.
Python
import re
import requests
def extract_challenge_params(url):
"""Extract Cloudflare Turnstile em staging page parameters."""
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 Chrome/120.0.0.0",
"Accept": "text/html,*/*;q=0.8",
"Accept-Language": "en-US,en;q=0.9",
}
response = requests.get(url, headers=headers, timeout=15, allow_redirects=False)
html = response.text
params = {
"status_code": response.status_code,
"cf_ray": response.headers.get("cf-ray", ""),
"is_challenge": response.status_code == 503,
}
if not params["is_challenge"]:
return params
# Extract Ray ID from page
ray_match = re.search(r"ray['\"]?\s*[:=]\s*['\"]([a-f0-9]+)['\"]", html, re.I)
if ray_match:
params["ray_id"] = ray_match.group(1)
# Extract challenge script URL
script_match = re.search(
r'src=["\'](/cdn-cgi/challenge-platform/[^"\']+)["\']', html
)
if script_match:
params["challenge_script"] = script_match.group(1)
# Extract challenge options
opt_match = re.search(r"_cf_chl_opt\s*=\s*\{([^}]+)\}", html)
if opt_match:
opt_text = opt_match.group(1)
# Parse individual options
for key in ["cType", "cRay", "cHash", "cTTimeMs", "cvId", "cFPWv"]:
val_match = re.search(
rf"{key}\s*:\s*['\"]?([^'\"', }}]+)", opt_text
)
if val_match:
params[key] = val_match.group(1)
# Extract form parameters
md_match = re.search(r'name=["\']md["\']\s+value=["\']([^"\']+)["\']', html)
if md_match:
params["md"] = md_match.group(1)
# Extract cookies from response
params["cookies"] = {
name: value
for name, value in response.cookies.items()
}
return params
# Usage
params = extract_challenge_params("https://protected-site.com")
if params["is_challenge"]:
print(f"Challenge type: {params.get('cType', 'unknown')}")
print(f"Ray ID: {params.get('ray_id', params['cf_ray'])}")
print(f"Min wait: {params.get('cTTimeMs', '?')}ms")
print(f"Script: {params.get('challenge_script', 'not found')}")
Node.js
const axios = require("axios");
async function extractChallengeParams(url) {
const response = await axios.get(url, {
headers: {
"User-Agent": "Mozilla/5.0 Chrome/120.0.0.0",
Accept: "text/html,*/*;q=0.8",
},
validateStatus: () => true,
maxRedirects: 0,
});
const html = response.data;
const params = {
statusCode: response.status,
cfRay: response.headers["cf-ray"] || "",
isChallenge: response.status === 503,
};
if (!params.isChallenge) return params;
// Extract challenge script URL
const scriptMatch = html.match(
/src=["'](\/cdn-cgi\/challenge-platform\/[^"']+)["']/
);
if (scriptMatch) params.challengeScript = scriptMatch[1];
// Extract challenge type
const typeMatch = html.match(/cType\s*:\s*['"]?(\w+)/);
if (typeMatch) params.challengeType = typeMatch[1];
// Extract timing
const timeMatch = html.match(/cTTimeMs\s*:\s*['"]?(\d+)/);
if (timeMatch) params.minWaitMs = parseInt(timeMatch[1]);
return params;
}
extractChallengeParams("https://protected-site.com").then(console.log);
Resolvendo o desafio com a API da CaptchaAI
Você não reimplementa nada disso: a CaptchaAI processa o desafio — página, JS, prova de trabalho e cookie — e devolve o resultado pela API:
import requests
import time
API_KEY = "YOUR_API_KEY"
def solve_turnstile_staging(target_url):
"""Solve Cloudflare Turnstile em staging page — CaptchaAI handles token flow."""
submit = requests.post("https://ocr.captchaai.com/in.php", data={
"key": API_KEY,
"method": "turnstile_staging",
"sitekey": "managed",
"pageurl": target_url,
"json": 1,
})
task_id = submit.json()["request"]
for _ in range(60):
time.sleep(5)
result = requests.get("https://ocr.captchaai.com/res.php", params={
"key": API_KEY,
"action": "get",
"id": task_id,
"json": 1,
}).json()
if result.get("status") == 1:
return result["request"]
raise TimeoutError("Challenge solve timed out")
# CaptchaAI handles the full flow:
# 1. Loads the challenge page
# 2. Executes JavaScript
# 3. Solves proof-of-work
# 4. Returns clearance token/cookies
token = solve_turnstile_staging("https://protected-site.com/login")
Equipes de QA no Brasil repetem esse fluxo antes de picos previsíveis como a Black Friday, rodando os testes em staging.example.com a partir de workers na região sa-east-1 (São Paulo) da AWS para medir a latência real. Como o ambiente usa dados fictícios, a LGPD não entra em jogo.
Escopo: rode esse fluxo só em domínios próprios ou ambientes autorizados — staging, contas de QA, dados fictícios.
Como depurar e corrigir falhas no desafio Cloudflare
Onde o fluxo costuma quebrar
| Ponto de falha | Sintoma | Causa raiz |
|---|---|---|
| A página de desafio não carrega | Timeout ou resposta vazia | Problema de rede ou proxy |
| O script não executa | Desafio entra em loop | APIs de JavaScript ausentes no ambiente |
| A prova de trabalho falha | Spinner infinito | Timeout no cálculo |
| Resposta rejeitada | Redirecionamento de volta ao desafio | Violação de tempo ou sinal de navegador incompatível |
Checklist de depuração passo a passo
A função abaixo reproduz o fluxo em etapas para isolar onde a automação trava:
def debug_challenge_flow(url, cookie_qa_validacao_cookie=None, user_agent=None):
"""Debug the challenge solve flow step by step."""
ua = user_agent or (
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 Chrome/120.0.0.0"
)
steps = []
# Step 1: Initial request
response = requests.get(
url,
headers={"User-Agent": ua, "Accept": "text/html,*/*;q=0.8"},
timeout=15,
allow_redirects=False,
)
steps.append({
"step": "initial_request",
"status": response.status_code,
"is_challenge": response.status_code == 503,
"cf_ray": response.headers.get("cf-ray", ""),
})
# Step 2: Test with cookie_qa_validacao
if cookie_qa_validacao_cookie:
session = requests.Session()
session.cookies.set("cookie_qa_validacao", cookie_qa_validacao_cookie)
session.headers["User-Agent"] = ua
response2 = session.get(url, timeout=15, allow_redirects=False)
steps.append({
"step": "with_clearance",
"status": response2.status_code,
"passed": response2.status_code == 200,
})
if response2.status_code != 200:
steps.append({
"step": "diagnosis",
"issue": "cookie_qa_validacao rejected",
"possible_causes": [
"Cookie expired",
"IP address changed",
"User-Agent mismatch",
"Cookie from different domain",
],
})
return steps
Erros frequentes e como corrigir
- "managed" mas a resolução falha — o site exige Turnstile, não o desafio em JavaScript: use o método
turnstileem vez deturnstile_staging. - cookie_qa_validacao funciona uma vez e depois é rejeitado — rotação de IP mudou seu endereço: fixe o IP durante a vida útil do cookie.
- "Just a moment..." nunca resolve — JavaScript bloqueado ou malformado: use a CaptchaAI em vez de resolver manualmente.
- Desafio reaparece a cada requisição — cookie_qa_validacao não está sendo enviado: garanta que os cookies persistam na sessão.
- Desafio diferente em caminhos diferentes — regras de WAF por caminho: resolva separadamente para cada caminho.
Perguntas frequentes
Preciso simular o desafio da Cloudflare em staging antes de ir para produção?
Vale a pena em automações longas: reproduzir o fluxo em staging revela problemas de timing e cookie antes da produção.
Por quanto tempo o cookie cookie_qa_validacao permanece válido?
Depende do site: 30 min por padrão, até 24 h para enterprise. Ao expirar, a próxima requisição recebe o desafio de novo.
Minha automação recebe o desafio de novo mesmo depois de validar o cookie. Por quê?
Geralmente é IP ou User-Agent incompatível: o cookie só vale para quem resolveu o desafio — rotação de proxy no meio da sessão é a causa mais comum.
É possível reproduzir esse fluxo sem executar JavaScript de verdade?
Não. A prova de trabalho e o sinal de navegador rodam no JavaScript da página — sem executá-lo, não existe token válido para devolver à Cloudflare.
A CaptchaAI resolve o desafio Cloudflare Turnstile automaticamente durante os testes?
Sim. A CaptchaAI processa o fluxo — página, JavaScript e prova de trabalho — e devolve o cookie de liberação pronto para uso.
Resumo
A página de desafio carrega Ray IDs, um script de orquestração e um objeto de opções que alimentam um fluxo de prova de trabalho, do qual nasce o cookie cookie_qa_validacao — preso ao IP e ao User-Agent, válido de 15 min a 24 h. Com a CaptchaAI, você não implementa esse protocolo: o solver cuida do fluxo e ajuda a apontar onde a automação travou.