API Tutorials

Cloudflare doğrulama sayfası parametreleri ve token akışı

Bir isteğiniz HTTP 503 ve Just a moment... sayfasıyla karşılanıyorsa, Cloudflare arka planda çok adımlı bir doğrulama akışı çalıştırıyordur: parametreleri toplar, JavaScript yürütür ve sonunda qa_session_cookie üretir. Parametreleri tanımak, iş akışınızın hangi adımda takıldığını görmenizi sağlar.

Senaryo tanıdık gelecek: İstanbul'daki bir ekip ödeme akışını test ediyor, doğrulama sayfası geliyor ama çerez ayarlanmıyor. Aşağıda önce parametreleri, sonra token akışını, en sonda da akışı CaptchaAI ile tek çağrıya indirmeyi göreceksiniz.

Bu rehberde ele alınanlar:

  • 503 sayfasındaki ray, md, r ve _cf_chl_opt alanları
  • Token akışının dokuz adımı
  • qa_session_cookie çerezinin IP ve UA bağı
  • Parametreleri kodla çıkarma ve akışı CaptchaAI'ye devretme

503 doğrulama sayfasında ne bulunur

Cloudflare doğrulama sunduğunda dönen HTML iskeleti sadedir: bir durum alanı, gizli bir form ve /cdn-cgi/challenge-platform/ altından yüklenen bir betik. Asıl iş betiğin içinde yürür.

<!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>

Sayfadaki anahtar parametreler

Akışı yöneten değerler üç yerde bulunur: HTML gövdesi, betik URL'si ve JavaScript yükü. Önce gövdedeki alanlar:

Parametre Açıklama İşlevi
ray Cloudflare Ray ID Benzersiz istek tanımlayıcısı; doğrulamayı özgün isteğe bağlar
md Doğrulama meta verisi Şifrelenmiş doğrulama durumu
r Yanıt token'ı JavaScript'in hesaplayıp doldurduğu yanıt
chl_opt Doğrulama seçenekleri Doğrulama betiğinin yapılandırması
cRay İkincil Ray Doğrulama takibi için ek ray değeri
cZone Doğrulama bölgesi Cloudflare bölge (zone) kimliği
cUPMDTk Zaman damgası Doğrulamanın verildiği an
cHash Bütünlük hash'i Değişmezlik doğrulaması

Doğrulama betiği URL'sindeki bileşenler

/cdn-cgi/challenge-platform/h/g/orchestrate/chl_page/v1?ray=ABC123
Bileşen Anlamı
/cdn-cgi/challenge-platform/ Cloudflare doğrulama altyapısı
h/g/ Doğrulama sürümü/varyantı
orchestrate/ Orkestrasyon uç noktası
chl_page/v1 Doğrulama sayfası sürümü
ray=ABC123 İsteğe bağlı Ray ID

JavaScript yükündeki parametreler

Betik çalışınca window._cf_chl_opt ek alanlar yükler: doğrulama tipi, zamanlama, şablon sürümü.

// 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',          // test profil yapılandırması version
    cTTimeMs: '4000',    // Minimum wait time (ms)
    cTplV: 5,            // Template version
    cLt: '...',          // Challenge lifetime
    cRq: {},             // Challenge request data
};

Token akışı: doğrulamadan izne dokuz adım

Akış istemci ile Cloudflare arasında dokuz adımda ilerler: betik tarayıcı sinyallerini toplar, proof-of-work hesaplar ve şifreli bir yanıt token'ı üretir; Cloudflare doğrularsa qa_session_cookie verir:


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 test profil yapılandırması:

      - canvas QA tanılama
      - 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 test profil yapılandırması + 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?
   - test profil yapılandırması 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

Çerez zaman çizelgesi

İlk istekte çerez yoktur; doğrulama çözülünce qa_session_cookie ayarlanır ve süresi dolana kadar sonraki istekleri geçirir:

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

Akıştaki çerezler arasında asıl belirleyici qa_session_cookie'dir:

Çerez İşlevi Ömrü Kapsam
__cf_bm Bot yönetimi oturum takibi 30 dakika Alan adı
qa_session_cookie Doğrulama izni kanıtı 15 dakika – 24 saat (yapılandırılabilir) Alan adı
__cflb Yük dengeleyici benzeşimi Oturum Alan adı
_cfuvid Benzersiz ziyaretçi kimliği Oturum Alan adı

qa_session_cookie taşınabilir bir çerez değildir; üç koşula bağlıdır:

  1. IP adresi — doğrulamayı çözen IP ile aynı olmalıdır
  2. User-Agent — doğrulama sırasında kullanılan UA ile eşleşmelidir
  3. Alan adı — yalnızca çerezi veren alan için geçerlidir
# ❌ 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

Parametreleri kodla çıkarma

Akışın nerede koptuğunu görmek için önce parametreleri okumanız gerekir. İki örnek 503 yanıtını yakalar ve ray, betik URL'si, doğrulama tipi ile bekleme süresini ayıklar.

Python

import re
import requests

def extract_challenge_params(url):
    """Extract Cloudflare doğrulama akışı 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);

CaptchaAI ile tek çağrıda çözme

Elle ayıklama teşhis için değerlidir; üretimde ise akışı tümüyle devretmek daha sürdürülebilirdir. CaptchaAI doğrulama sayfasını yükler, JavaScript'i yürütür, proof-of-work'ü çözer ve izin token'ını döndürür. Size kalan tek iş görevi gönderip sonucu sorgulamaktır — örnek, her 5 saniyede bir res.php'yi yoklar:

Fiyatlandırma thread tabanlıdır: BASIC ($15/ay, 5 thread) ilk entegrasyon için yeterli, hacim arttıkça ADVANCE ($90/ay, 50 thread) gibi planlara geçersiniz. TL dalgalansa da aylık maliyet USD üzerinden öngörülebilir kalır.

import requests
import time

API_KEY = "YOUR_API_KEY"

def solve_cloudflare_challenge(target_url):
    """Solve Cloudflare doğrulama akışı 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")

Doğrulama başarısızlıklarını ayıklama

Akış koptuğunda belirti genellikle şu noktalardan birine işaret eder:

Kırılma noktası Belirti Olası neden
Doğrulama sayfası yüklenmiyor Zaman aşımı veya boş yanıt Ağ/proxy sorunu
Betik yürütülemiyor Doğrulama döngüye giriyor Eksik JavaScript API'leri
Proof-of-work tamamlanmıyor Sonsuz dönen simge Hesaplama zaman aşımı
Yanıt reddediliyor Doğrulamaya geri yönlendirme Zamanlama ihlali veya profil uyuşmazlığı
qa_session_cookie ayarlanmıyor Çözümden sonra çerez yok Yanıt ayrıştırma hatası
qa_session_cookie reddediliyor Sonraki istekte 403 IP veya UA uyuşmazlığı

Adım adım hata ayıklama

def debug_challenge_flow(url, qa_session_cookie_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 qa_session_cookie_cookie:
        session = requests.Session()
        session.cookies.set("qa_session_cookie", qa_session_cookie_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

Sık karşılaşılan sorunlar ve çözümleri

Belirti Neden Çözüm
Tip "managed" ama çözüm başarısız Doğrulama JS değil Turnstile istiyor cloudflare_challenge yerine turnstile yöntemini deneyin
qa_session_cookie bir kez çalışıp sonra reddediliyor IP rotasyonu IP'nizi değiştirdi Çerez ömrü boyunca IP'yi sabit tutun
"Just a moment..." sayfası hiç çözülmüyor JavaScript engellenmiş veya bozuk Elle çözüm yerine CaptchaAI kullanın
Her istekten sonra doğrulama yeniden çıkıyor qa_session_cookie gönderilmiyor Çerezleri oturumda kalıcı tutun
Farklı yollarda farklı doğrulama Yola özel WAF kuralları Her yolu ayrı ayrı çözün

Sık sorulan sorular

cloudflare_challenge yöntemi ne zaman turnstile'dan farklıdır?

cloudflare_challenge, JavaScript proof-of-work gerektiren tam sayfa doğrulaması (Just a moment... sayfası) içindir. Sayfada gömülü bir Cloudflare Turnstile widget'ı görüyorsanız turnstile yöntemini kullanın. cType managed olduğu halde çözüm başarısızsa doğrulama Turnstile'a düşmüş olabilir.

Neredeyse her zaman IP veya User-Agent uyuşmazlığından. Çerez, çözümü yapan IP'ye ve UA'ya bağlıdır; farklı bir çıkıştan veya farklı UA ile gönderilen istekte geçersiz sayılır. Çözüm ve sonraki istekler için aynı oturumu ve IP'yi kullanın.

Bu doğrulamayı JavaScript çalıştırmadan çözebilir miyim?

Hayır. Proof-of-work ve tarayıcı sinyalleri yalnızca JavaScript yürütülerek hesaplanır; salt HTTP isteğiyle üretilemez. CaptchaAI bunu gerçek tarayıcılarla çalıştırır.

Doğrulama her istekte yeniden çıkıyorsa ne yapmalıyım?

Genellikle qa_session_cookie sonraki isteklere eklenmiyordur. İstemcinizin çerezleri kalıcı tuttuğundan emin olun; requests.Session ya da eşdeğeri kullanın. Çerez süresi (15 dakika–24 saat) dolduğunda yeni doğrulama beklemek normaldir.

Özet

Cloudflare doğrulama sayfaları; ray, doğrulama betikleri, _cf_chl_opt nesnesi ve form alanları üzerinden proof-of-work token akışını yürütür. Akış, IP'ye ve User-Agent'a bağlı, 15 dakika–24 saat geçerli bir qa_session_cookie çerezi üretir. CaptchaAI bu parametreleri elle ayrıştırma yükünü kaldırır; çözücü akışın tamamını yönetir. Hata ayıklarken parametreleri tanımak, akışın nerede koptuğunu gösterir.

İlgili makaleler

Bu makale için yorumlar devre dışı bırakılmıştır.