Tutoriels API

Paramètres de la page Cloudflare Challenge et flux de jetons

Lorsque Cloudflare présente une page de défi, un flux de jetons complexe commence : des paramètres de page initiaux jusqu'au cookie cf_clearance final en passant par l'exécution de JavaScript. Comprendre ces paramètres vous aide à diagnostiquer les échecs de résolution, à déboguer les flux d'automatisation et à choisir la bonne approche de résolution.


Anatomie de la page de défi

Une page de défi Cloudflare (HTTP 503) contient plusieurs éléments clés :

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

Paramètres clés

Dans la page du défi

Paramètre Nom Objectif
ray ID de rayon Cloudflare Identifiant de demande unique, lie le défi à la demande d'origine
md Métadonnées du défi État de défi chiffré
r Jeton de réponse Réponse calculée (remplie par JavaScript)
chl_opt Options de défi Configuration pour le script de défi
cRay Rayon de défi Rayon secondaire pour le suivi des défis
cZone Zone de défi ID de zone Cloudflare
cUPMDTk Horodatage Heure d'émission du défi
cHash Hachage de défi Validation de l'intégrité

Dans l'URL du script de défi

/cdn-cgi/challenge-platform/h/g/orchestrate/chl_page/v1?ray=ABC123
Composant Signification
/cdn-cgi/challenge-platform/ Infrastructure de défi Cloudflare
h/g/ Version du défi/variant
orchestrate/ Point de terminaison de l’orchestration du défi
chl_page/v1 Version de la page du défi
ray=ABC123 Demander la liaison Ray ID

Dans la charge utile JavaScript

Le script de challenge charge des paramètres supplémentaires :

// 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
};

Flux de jetons du défi à l'autorisation

Déroulement étape par étape


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: cf_clearance=...; path=/; expires=...
   + HTTP redirect to original URL
   ↓

8. CLIENT → CLOUDFLARE
   GET /protected-page
   Cookie: cf_clearance=...
   ↓

9. CLOUDFLARE → CLIENT
   HTTP 200 + Protected content

Chronologie des cookies

Request 1: No cookies
    → Challenge page (503)
    → __cf_bm cookie set

Challenge solve:
    → cf_clearance cookie set

Request 2+: cf_clearance + __cf_bm
    → Content served (200)

After ~30 mins: cf_clearance expires
    → Next request triggers new challenge

Cookies de défi

Biscuit Objectif Durée de vie Portée
__cf_bm Suivi des sessions de gestion des robots 30 minutes Domaine
cf_clearance Preuve d'autorisation de défi 15 min – 24 h (configurable) Domaine
__cflb Affinité de l'équilibreur de charge Séance Domaine
_cfuvid Identifiant unique du visiteur Séance Domaine

Contraintes des cookies cf_clearance

Le cookie cf_clearance est lié à :

  1. Adresse IP – Doit provenir de la même adresse IP que celle qui a résolu le défi
  2. User-Agent – Doit correspondre à l'UA utilisé pendant le défi.
  3. Domaine – Valable uniquement pour le domaine qui l'a émis
# ❌ FAILS — IP mismatch
# Solve challenge from IP A, then use cf_clearance 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

Extraire les paramètres du défi

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')}")

Noeud.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);

Résolution avec CaptchaAI

CaptchaAI gère l'intégralité du flux de jetons en interne : vous n'avez pas besoin d'extraire manuellement les paramètres de défi :

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")

Échecs du défi de débogage

Points de défaillance courants

Point de défaillance Symptôme Cause profonde
La page du défi ne se charge pas Délai d'attente ou réponse vide Problème de réseau/proxy
Le script ne parvient pas à s'exécuter Boucles de défi API JavaScript manquantes
La preuve de travail échoue Spinner infini Délai d'attente de calcul
Réponse rejetée Rediriger vers le défi Violation du timing ou non-concordance des empreintes digitales
cf_clearance non défini Cookie manquant après la résolution Erreur d'analyse de la réponse
cf_clearance rejeté 403 sur demande ultérieure Incompatibilité IP ou UA

Liste de contrôle de débogage

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 cf_clearance
    if cf_clearance_cookie:
        session = requests.Session()
        session.cookies.set("cf_clearance", 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": "cf_clearance rejected",
                "possible_causes": [
                    "Cookie expired",
                    "IP address changed",
                    "User-Agent mismatch",
                    "Cookie from different domain",
                ],
            })

    return steps

Dépannage

Symptôme Parce que Corriger
Le type de défi est « géré » mais la résolution échoue Le défi nécessite un tourniquet, pas un défi JS Essayez la méthode turnstile au lieu de cloudflare_challenge
cf_clearance fonctionne une fois, puis rejeté La rotation IP a changé votre IP Pin IP pour la durée de vie de l'autorisation
La page "Juste un instant..." ne se résout jamais JavaScript bloqué ou mal formé Utilisez CaptchaAI au lieu de la résolution manuelle
Le défi réapparaît après chaque demande cf_clearance n'est pas envoyé Assurez-vous que les cookies sont conservés en session
Différents défis sur différents chemins Règles WAF par chemin Résoudre pour chaque chemin séparément

Questions fréquemment posées

Il s'agit d'un jeton crypté contenant la preuve de résolution, le hachage IP, le hachage UA et le délai d'expiration. Vous ne pouvez pas le décoder ou le falsifier : seul le Edge de Cloudflare peut le valider.

Combien de temps dure cf_clearance ?

Les opérateurs du site configurent la durée de vie. La valeur par défaut est 30 minutes. La plage est de 15 minutes à 24 heures. Les clients Entreprise peuvent définir des valeurs personnalisées.

Puis-je résoudre le défi sans exécuter JavaScript ?

Non. Le défi nécessite que JavaScript calcule la preuve de travail et l'empreinte digitale du navigateur. CaptchaAI gère cela en interne en utilisant de vrais navigateurs.

Que se passe-t-il si le Ray ID change ?

Chaque requête obtient un nouveau Ray ID. Le défi est lié au Ray ID au moment de la page du défi. Une fois cf_clearance émis, le Ray ID n'est plus pertinent.

Puis-je réutiliser cf_clearance sur différents domaines ?

Non. cf_clearance s'étend au domaine. Chaque domaine nécessite son propre cookie de résolution de défi et son propre cookie d'autorisation.


Résumé

Les pages de défi Cloudflare contiennent des ID Ray, des scripts de défi, des objets d'options et des paramètres de formulaire qui pilotent un flux de jetons de preuve de travail. Le flux produit un cookie cf_clearance lié à l'IP et au User-Agent, valable de 15 minutes à 24 heures. AvecCaptchaAI, vous n'avez pas besoin d'analyser ces paramètres manuellement : le solveur gère l'intégralité du flux. Pour le débogage, la compréhension des paramètres permet d'identifier l'endroit où le flux s'interrompt.

Articles connexes

Les commentaires sont désactivés pour cet article.