Saat Cloudflare menyajikan halaman challenge, alur token yang kompleks dimulai — dari parameter halaman awal melalui eksekusi JavaScript hingga cookie qa_validation_cookie akhir. Memahami parameter ini membantu mendiagnosis kegagalan solving, debug alur otomasi, dan memilih pendekatan solving yang tepat.
Anatomi Halaman Challenge
Halaman challenge Cloudflare (HTTP 503) berisi beberapa elemen utama:
<!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>
Parameter kunci
Di halaman tantangan
| Parameter | Nama | Tujuan |
|---|---|---|
ray |
ID Cloudflare Ray | Pengidentifikasi permintaan unik, menghubungkan tantangan dengan permintaan asli |
md |
Metadata tantangan | Status tantangan terenkripsi |
r |
Token respons | Jawaban yang dihitung (diisi dengan JavaScript) |
chl_opt |
Opsi tantangan | Konfigurasi untuk skrip tantangan |
cRay |
Sinar tantangan | Sinar sekunder untuk pelacakan tantangan |
cZone |
Zona tantangan | ID zona Cloudflare |
cUPMDTk |
Stempel waktu | Waktu penerbitan tantangan |
cHash |
Tantang hash | Validasi integritas |
Di URL skrip tantangan
/cdn-cgi/challenge-platform/h/g/orchestrate/chl_page/v1?ray=ABC123
| Komponen | Artinya |
|---|---|
/cdn-cgi/challenge-platform/ |
Cloudflare menantang infrastruktur |
h/g/ |
Versi tantangan/variant |
orchestrate/ |
Tantang titik akhir orkestrasi |
chl_page/v1 |
Versi halaman tantangan |
ray=ABC123 |
Minta pengikatan Ray ID |
Dalam muatan JavaScript
Skrip tantangan memuat parameter tambahan:
// 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', // sinyal browser version
cTTimeMs: '4000', // Minimum wait time (ms)
cTplV: 5, // Template version
cLt: '...', // Challenge lifetime
cRq: {}, // Challenge request data
};
Aliran Token dari Challenge ke Izin
Alur Langkah demi Langkah
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 sinyal browser:
- 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 sinyal browser + 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?
- sinyal browser consistent with real browser?
- No replay (nonce check)?
↓
7. CLOUDFLARE → CLIENT
HTTP 200 + Set-Cookie: qa_validation_cookie=...; path=/; expires=...
+ HTTP redirect to original URL
↓
8. CLIENT → CLOUDFLARE
GET /protected-page
Cookie: qa_validation_cookie=...
↓
9. CLOUDFLARE → CLIENT
HTTP 200 + Protected content
Garis waktu kue
Request 1: No cookies
→ Challenge page (503)
→ __cf_bm cookie set
Challenge solve:
→ qa_validation_cookie cookie set
Request 2+: qa_validation_cookie + __cf_bm
→ Content served (200)
After ~30 mins: qa_validation_cookie expires
→ Next request triggers new challenge
Cookie Challenge
| Cookie | Tujuan | Lifetime | Ruang Lingkup |
|---|---|---|---|
__cf_bm |
Tracking sesi bot management | 30 menit | Domain |
qa_validation_cookie |
Bukti izin challenge | 15 menit – 24 jam (dapat dikonfigurasi) | Domain |
__cflb |
Afinitas load balancer | Sesi | Domain |
_cfuvid |
ID pengunjung unik | Sesi | Domain |
Batasan Cookie qa_validation_cookie
Cookie qa_validation_cookie terikat pada:
- Alamat IP – Harus dari IP yang sama yang men-solve challenge
- User-Agent — Harus cocok dengan UA yang digunakan selama challenge
- Domain – Hanya berlaku untuk domain yang menerbitkannya
# ❌ FAILS — IP mismatch
# Solve challenge from IP A, then use qa_validation_cookie 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
Mengekstraksi Parameter Challenge
Python
import re
import requests
def extract_challenge_params(url):
"""Extract Cloudflare challenge 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);
Solve dengan CaptchaAI
CaptchaAI menangani seluruh aliran token secara internal – Anda tidak perlu mengekstrak parameter challenge secara manual:
import requests
import time
API_KEY = "YOUR_API_KEY"
def solve_cloudflare_challenge(target_url):
"""Solve Cloudflare challenge page — CaptchaAI handles token flow."""
submit = requests.post("https://ocr.captchaai.com/in.php", data={
"key": API_KEY,
"method": "cloudflare_challenge",
"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_cloudflare_challenge("https://protected-site.com/login")
Debug Kegagalan Challenge
Titik Kegagalan Umum
| Titik Kegagalan | Gejala | Akar Penyebab |
|---|---|---|
| Halaman challenge tidak dimuat | Timeout atau respons kosong | Masalah jaringan/proxy |
| Skrip gagal dieksekusi | Challenge loop | JavaScript API tidak tersedia |
| Proof-of-work gagal | Spinner tanpa batas | Timeout komputasi |
| Respons ditolak | Redirect kembali ke challenge | Pelanggaran timing atau sinyal browser mismatch |
| qa_validation_cookie tidak di-set | Cookie hilang setelah solve | Error parsing respons |
| qa_validation_cookie ditolak | 403 pada request berikutnya | IP atau UA mismatch |
Checklist Debug
def debug_challenge_flow(url, cf_clearance_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 qa_validation_cookie
if cf_clearance_cookie:
session = requests.Session()
session.cookies.set("qa_validation_cookie", cf_clearance_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": "qa_validation_cookie rejected",
"possible_causes": [
"Cookie expired",
"IP address changed",
"User-Agent mismatch",
"Cookie from different domain",
],
})
return steps
Pemecahan Masalah
| Gejala | Penyebab | Solusi |
|---|---|---|
| Tipe challenge “managed” tapi solving gagal | Challenge perlu Turnstile, bukan JS challenge | Coba metode turnstile daripada cloudflare_challenge |
| qa_validation_cookie berfungsi sekali lalu ditolak | Rotasi IP mengubah IP Anda | Pin IP untuk durasi izin |
| Halaman “Just a moment...” tidak pernah resolve | JavaScript diblokir atau format salah | Gunakan CaptchaAI daripada solving manual |
| Challenge muncul kembali setiap request | qa_validation_cookie tidak terkirim | Pastikan cookie persisten dalam sesi |
| Challenge berbeda di path berbeda | Aturan WAF per path | Solve untuk setiap path secara terpisah |
Pertanyaan Umum
Apa yang ada di dalam cookie qa_validation_cookie?
Ini adalah token terenkripsi yang berisi bukti penyelesaian, hash IP, hash UA, dan waktu kedaluwarsa. Anda tidak bisa decode atau memalsukan — hanya edge Cloudflare yang bisa memvalidasinya.
Berapa lama qa_validation_cookie bertahan?
Operator situs mengonfigurasi lifetime-nya. Default 30 menit. Rentangnya 15 menit hingga 24 jam. Pelanggan enterprise bisa menetapkan nilai kustom.
Bisakah saya solve challenge ini tanpa eksekusi JavaScript?
Tidak. Challenge ini memerlukan JavaScript untuk menghitung proof-of-work dan browser sinyal browser. CaptchaAI menangani ini secara internal menggunakan browser nyata.
Apa yang terjadi jika Ray ID berubah?
Setiap request mendapat Ray ID baru. Challenge terikat ke Ray ID saat halaman challenge diterbitkan. Setelah qa_validation_cookie dikeluarkan, Ray ID tidak lagi relevan.
Bisakah saya gunakan kembali qa_validation_cookie di domain berbeda?
Tidak. qa_validation_cookie dilingkupi oleh domain. Setiap domain memerlukan solve challenge dan cookie izinnya sendiri.
Ringkasan
Halaman challenge Cloudflare berisi Ray ID, skrip challenge, objek opsi, dan parameter form yang mendorong aliran token proof-of-work. Alur tersebut menghasilkan cookie qa_validation_cookie yang terikat pada IP dan User-Agent, valid selama 15 menit hingga 24 jam. Dengan CaptchaAI, Anda tidak perlu parsing parameter ini secara manual — solver menangani seluruh aliran. Untuk debug, memahami parameter membantu mengidentifikasi di mana aliran terputus.
Artikel Terkait
- Cloudflare Challenge vs Turnstile: Cara Mendeteksi
- Cloudflare Managed vs Interactive Challenge
- Cloudflare Turnstile 403 Setelah Token — Perbaikan