Trang Cloudflare Challenge (HTTP 503) là một chuỗi tham số (ray, md, r, chl_opt...) được JavaScript âm thầm tính toán rồi đổi lấy cookie clearance. Nắm rõ từng tham số là cách nhanh nhất để biết request đang kẹt ở đâu — trang không tải, proof-of-work không xong, hay cookie bị từ chối ở request kế tiếp — kèm code Python và Node.js để tự trích xuất, tự debug.
Đọc xong bài này bạn sẽ:
- Đọc hiểu từng tham số trong HTML, URL script và payload JavaScript của trang thử thách
- Theo được đường đi đầy đủ của token, từ request đầu tiên đến cookie clearance
- Tự viết code Python/Node.js trích xuất tham số cho bất kỳ domain nào
- Biết chính xác lỗi nào gây ra triệu chứng nào khi luồng giải không ra
Cấu trúc HTML của trang Cloudflare Challenge
Khi edge Cloudflare nghi ngờ request, nó trả HTTP 503 kèm trang HTML dưới đây thay vì nội dung thật. Ba mảnh đáng chú ý:
- Khối
challenge-formchứa tham số ẩnmd/r - Thẻ
<script>trỏ tớiorchestrate/chl_page— nơi phép tính proof-of-work diễn ra - Nội dung hiển thị "Checking if the site connection is secure" trong lúc chờ
Toàn bộ HTML gốc:
<!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>
Từng tham số trong luồng nghĩa là gì
Tham số trên trang HTML
ray(Cloudflare Ray ID) — định danh request, gắn với challenge gốcmd(metadata) — trạng thái thử thách, đã mã hóar(response token) — đáp án do JavaScript tính và điền vàochl_opt(tùy chọn) — cấu hình script thử tháchcRay(ray phụ) — theo dõi riêng cho thử tháchcZone(zone ID) — ID zone Cloudflare của domaincUPMDTk(timestamp) — thời điểm phát hành thử tháchcHash(hash) — xác thực tính toàn vẹn
Tham số trong URL của script thử thách
/cdn-cgi/challenge-platform/h/g/orchestrate/chl_page/v1?ray=ABC123
Bóc tách từng đoạn: /cdn-cgi/challenge-platform/ là hạ tầng thử thách của Cloudflare, h/g/ là phiên bản/variant, orchestrate/ là endpoint điều phối, chl_page/v1 là phiên bản trang thử thách, còn ray=ABC123 gắn URL này với đúng request đã tạo ra nó.
Tham số trong payload JavaScript
Script thử thách nạp thêm một loạt tham số vào biến toàn cục — đây là phần mà proof-of-work và fingerprint thật sự dùng để tính response token:
// 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', // Fingerprint version
cTTimeMs: '4000', // Minimum wait time (ms)
cTplV: 5, // Template version
cLt: '...', // Challenge lifetime
cRq: {}, // Challenge request data
};
Đường đi của token: từ thử thách đến cookie clearance
Chín bước trên dây, đúng thứ tự xảy ra giữa client và edge Cloudflare:
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 fingerprint:
- 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 fingerprint + 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?
- Fingerprint consistent with real browser?
- No replay (nonce check)?
↓
7. CLOUDFLARE → CLIENT
HTTP 200 + Set-Cookie: qa_session_cookie=...; path=/; expires=...
+ HTTP redirect to original URL
↓
8. CLIENT → CLOUDFLARE
GET /protected-page
Cookie: qa_session_cookie=...
↓
9. CLOUDFLARE → CLIENT
HTTP 200 + Protected content
Mấu chốt: bước 4 chạy toàn bộ trong trình duyệt (fingerprint, proof-of-work, timing); Cloudflare ở bước 6 chỉ xác thực kết quả có khớp độ khó và thời gian hợp lý hay không.
Cookie nào sống được bao lâu trong toàn bộ chuỗi trên:
Request 1: No cookies
→ Challenge page (503)
→ __cf_bm cookie set
Challenge solve:
→ qa_session_cookie cookie set
Request 2+: qa_session_cookie + __cf_bm
→ Content served (200)
After ~30 mins: qa_session_cookie expires
→ Next request triggers new challenge
Bốn cookie liên quan tới Cloudflare Challenge
__cf_bm— theo dõi phiên cho bot management, sống 30 phút, phạm vi domainqa_session_cookie— bằng chứng đã vượt qua thử thách, sống 15 phút – 24 giờ tùy site cấu hình, phạm vi domain__cflb— ghim request về cùng backend (load balancer), sống theo phiên, phạm vi domain_cfuvid— ID khách truy cập duy nhất, sống theo phiên, phạm vi domain
Vì sao cookie clearance không copy-paste được giữa các máy
Cookie qa_session_cookie bị ràng buộc với ba yếu tố của phiên đã giải:
- Địa chỉ IP — phải đến từ đúng IP đã giải thử thách đó
- User-Agent — phải khớp UA lúc giải
- Domain — chỉ hợp lệ cho domain đã phát hành
Đây là lý do phổ biến nhất khiến cookie "vừa lấy xong" bị 403 ở request kế tiếp: đổi proxy giữa chừng, hoặc giải bằng UA khác với UA gửi request thật.
# ❌ FAILS — IP mismatch
# Solve challenge from IP A, then use qa_session_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
Tự trích xuất tham số bằng code
Muốn xem chính xác Cloudflare gửi tham số nào cho một domain, đơn giản nhất là tự parse response. Hai đoạn dưới lấy ray, cType, cTTimeMs và script thử thách trực tiếp từ HTML trả về.
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')}")
Bản Node.js làm cùng việc, hữu ích nếu automation stack của bạn đã chạy trên axios/playwright:
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);
Để CaptchaAI lo luồng token này
Tự parse tham số như trên tốt cho debug, nhưng tự động hóa proof-of-work và fingerprint trong production là bài toán khác — đổi nhỏ ở cTplV hay logic cHash đủ để phá code tự viết. Một đội QA outsourcing ở TP.HCM theo dõi giá trên nhiều sàn thương mại điện tử mỗi đêm hay gặp đúng cảnh này: script chạy ổn tuần này, tuần sau Cloudflare đổi template và pipeline giải thử thách gãy hàng loạt.
CaptchaAI xử lý nội bộ toàn bộ luồng trên bằng method=cloudflare_challenge — gửi pageurl, polling res.php, nhận thẳng token clearance mà không cần tự bảo trì logic trích xuất:
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")
CaptchaAI thường trả token trong dưới 15 giây với tỷ lệ giải thành công cao trên challenge dạng "managed" tiêu chuẩn — vẫn nên poll res.php theo vòng lặp như code trên thay vì đặt timeout cố định, vì thời gian thực tế phụ thuộc độ khó Cloudflare áp cho từng request.
Vì sao thử thách của bạn giải không ra
Bảy điểm nghẽn hay gặp nhất, xếp theo triệu chứng dễ nhận ra trước:
- Timeout hoặc response rỗng → trang thử thách không tải được → thường là sự cố network/proxy
- Lặp vòng thử thách liên tục → script không chạy được → thiếu JavaScript API cần thiết
- Spinner quay vô hạn → proof-of-work không xong → hết thời gian tính toán
- Bị chuyển hướng ngược về trang thử thách → response bị từ chối → sai lệch timing hoặc fingerprint không khớp
- Thiếu cookie sau khi giải xong → cookie clearance không được set → lỗi khi parse response
- 403 ở request kế tiếp → cookie clearance bị từ chối → IP hoặc User-Agent không khớp
- Loại "managed" nhưng giải mãi không xong → trang thực chất cần Turnstile, thử method
turnstilethay vìcloudflare_challenge
Checklist tự 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_session_cookie
if cf_clearance_cookie:
session = requests.Session()
session.cookies.set("qa_session_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_session_cookie rejected",
"possible_causes": [
"Cookie expired",
"IP address changed",
"User-Agent mismatch",
"Cookie from different domain",
],
})
return steps
Cách xử lý bốn tình huống hay gặp nhất
Bên cạnh checklist ở trên, đây là cách xử lý nhanh cho các ca thường gặp trong thực tế:
Cookie xoay vòng bị từ chối → ghim một IP cố định trong vòng đời cookie thay vì đổi proxy giữa chừng.
Trang "Just a moment..." không bao giờ qua → JavaScript đang bị chặn hoặc chạy sai định dạng, dùng CaptchaAI thay vì tự dựng trình duyệt để giải.
Thử thách hiện lại ở mỗi request → cookie clearance không được gửi kèm, đảm bảo nó được giữ xuyên suốt session.
Mỗi path trả về một thử thách khác nhau → đó là rule WAF áp riêng theo từng path, phải giải riêng cho từng path chứ không dùng chung một cookie.
Câu hỏi thường gặp
Bốn câu dev hay hỏi nhất về cookie clearance và luồng token của Cloudflare Challenge:
Cookie clearance của Cloudflare Challenge chứa gì bên trong?
Một token mã hóa, gộp bằng chứng đã giải proof-of-work, hash IP, hash User-Agent và thời điểm hết hạn. Bạn không giải mã hay giả mạo được — chỉ edge Cloudflare mới xác thực.
Vì sao thử thách cứ hiện lại dù mới giải xong?
Ba nguyên nhân hay gặp nhất, xếp theo tần suất:
- Cookie không được lưu giữa các request trong cùng session
- Proxy đổi IP giữa lúc giải và lúc dùng cookie
- User-Agent hai bước không khớp nhau
Cả ba đều bắt nguồn từ ràng buộc IP + UA đã nói ở phần cookie phía trên.
Có cách nào giải thử thách mà không cần chạy JavaScript không?
Không. Proof-of-work và fingerprint trình duyệt bắt buộc chạy JavaScript thật. CaptchaAI xử lý nội bộ bằng trình duyệt thật, nên bạn không cần tự dựng môi trường JS.
Giải Cloudflare Challenge bằng CaptchaAI tốn bao nhiêu thread?
CaptchaAI tính phí theo thread chạy đồng thời, không theo mỗi lần giải. Plan BASIC ($15/tháng, 5 thread) đủ cho cloudflare_challenge quy mô nhỏ.
Cần polling nhiều domain song song thì tăng lên ADVANCE ($90/tháng, 50 thread) trở lên theo tải thực tế.
Tóm lại
- Cookie clearance ràng buộc IP + User-Agent, sống 15 phút đến 24 giờ tùy cấu hình site
- Hiểu tham số giúp xác định chính xác luồng gãy ở bước nào khi debug
- Ở quy mô lớn, để CaptchaAI xử lý trọn luồng qua
method=cloudflare_challengethay vì tự bảo trì logic trích xuất mỗi khi Cloudflare đổi template