Hintergründe

Cloudflare WAF-Regeln, die CAPTCHA-Abfragen auslösen

Taucht bei automatisiertem Zugriff plötzlich eine Cloudflare-Abfrage auf, steckt fast immer eine einzelne WAF-Regel dahinter. Cloudflares Web Application Firewall bewertet jede Anfrage anhand weniger Attribute – IP, Land, Pfad, Bot-Score, Header – und entscheidet, ob sie durchgelassen, geprüft oder blockiert wird. Dieser Artikel zeigt, welche Regeltypen eine Abfrage erzeugen, wie Sie die Regel aus der HTTP-Antwort ablesen und wie Sie die Challenge per API lösen.


Welche WAF-Aktionen ein CAPTCHA erzeugen

Cloudflare-WAF-Regeln kennen mehrere Aktionen. Nur drei führen zu einer lösbaren Challenge, der Rest lässt durch oder blockiert:

WAF-Aktion Was passiert HTTP-Status CaptchaAI-Methode
Managed Challenge Cloudflare entscheidet adaptiv: unsichtbar, Turnstile oder JS-Challenge 503 turnstile
JS Challenge JavaScript-Prüfseite (rund 5 Sekunden) 503 cloudflare_challenge
Interactive Challenge Klassisches CAPTCHA (veraltet) 403 turnstile
Block Harter 403, keine Challenge 403 – (nicht lösbar)
Allow Anfrage durchlassen, keine Prüfung 200
Skip Verbleibende WAF-Regeln überspringen 200
Log Nur Protokolleintrag, keine Aktion 200

Kurz: Ein 503 ist lösbar, ein reiner 403-Block nicht.

Managed Challenge – der Standardfall

Managed Challenge ist Cloudflares empfohlene und häufigste Aktion. Statt eines festen Typs wählt Cloudflare pro Besucher:

WAF rule matches → Managed Challenge triggered
    ↓
Cloudflare evaluates visitor:
  ├─ Low risk → Invisible pass (no visible challenge)
  ├─ Medium risk → Turnstile widget (click to verify)
  └─ High risk → JavaScript challenge page
    ↓
Successful → cf_clearance cookie issued

Derselbe Endpunkt lässt mal unsichtbar durch, mal zeigt er Turnstile oder eine JS-Seite – unberechenbar wirkend, aber der Risikologik folgend.


Typische WAF-Regelmuster im Überblick

Seitenbetreiber formulieren WAF-Regeln in Cloudflares Ausdruckssprache. Sechs Muster lösen am häufigsten eine Abfrage aus:

  • Bot-Score – niedrige Werte
  • Herkunftsland – bestimmte Regionen
  • URL-Pfad – z. B. /login, /api/
  • Anfragerate – zu viele Aufrufe
  • HTTP-Header – fehlend oder auffällig
  • Kombinationen – mehrere Bedingungen

Regeln nach Bot-Score

# Challenge traffic with low bot scores
(cf.bot_management.score lt 30)
→ Action: Managed Challenge

# Challenge non-verified bots
(cf.bot_management.score lt 50 and not cf.bot_management.verified_bot)
→ Action: JS Challenge

Bot-Score-Regeln sind der häufigste Auslöser für Automatisierungstools. Die API-Solver von CaptchaAI nutzen echte Browser und erreichen so Bot-Scores auf Nutzerniveau.

Regeln nach Herkunftsland

# Challenge traffic from specific countries
(ip.geoip.country in {"CN" "RU" "VN" "IN"})
→ Action: Managed Challenge

# Block specific regions entirely
(ip.geoip.country eq "XX")
→ Action: Block
  • Ein Proxy im zulässigen Land umgeht die Länderregel – eine Bot-Score- oder Pfadregel greift weiter.

Regeln nach URL-Pfad

# Challenge login page access
(http.request.uri.path eq "/login" or http.request.uri.path eq "/signup")
→ Action: Managed Challenge

# Challenge API endpoints
(http.request.uri.path contains "/api/")
→ Action: JS Challenge

Regeln nach Anfragerate

# Challenge after high request rate
(cf.threat_score gt 10 and http.request.uri.path contains "/search")
→ Action: Managed Challenge

Regeln nach HTTP-Headern

# Challenge requests with no Accept-Language header
(not http.request.headers["accept-language"])
→ Action: JS Challenge

# Challenge requests with suspicious UA
(http.user_agent contains "python" or http.user_agent contains "curl")
→ Action: Managed Challenge

Zusammengesetzte Bedingungen

# Multiple conditions
(cf.bot_management.score lt 30
 and http.request.uri.path contains "/api/"
 and ip.geoip.country ne "US")
→ Action: JS Challenge

Häufiger Fehler: ein Standard-User-Agent wie python-requests oder ein fehlender Accept-Language-Header greift oft, bevor Bot-Score oder Land überhaupt zählen.


Die auslösende Regel erkennen

Erscheint eine Abfrage, lässt sich die Regel meist aus der HTTP-Antwort ableiten:

Aus den HTTP-Headern

import requests

def check_cloudflare_rule_info(url):
    """Extract WAF rule information from Cloudflare challenge response."""
    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)

    info = {
        "status": response.status_code,
        "cf_ray": response.headers.get("cf-ray", ""),
        "cf_cache_status": response.headers.get("cf-cache-status", ""),
        "server": response.headers.get("server", ""),
    }

    # Challenge-specific info
    html = response.text

    if response.status_code == 503:
        if "jschl" in html:
            info["challenge_type"] = "JS Challenge (IUAM or WAF rule)"
        elif "challenge-platform" in html:
            info["challenge_type"] = "Managed Challenge"
        elif "cf-turnstile" in html:
            info["challenge_type"] = "Turnstile (Managed Challenge)"

    elif response.status_code == 403:
        if "cf-ray" in str(response.headers):
            info["challenge_type"] = "WAF Block (no challenge)"
        else:
            info["challenge_type"] = "Origin 403 (not Cloudflare)"

    return info

Die HTML-Marker (jschl, challenge-platform, cf-turnstile) verraten dann den konkreten Challenge-Typ.

Über die Cloudflare-Ray-ID

Jede Cloudflare-Antwort trägt einen cf-ray-Header. Nur der Seitenbetreiber kann diese Ray-ID im Dashboard (Security > Events) auflösen und Regel samt Aktion nachschlagen. Extern hilft nur die Rückwärtsanalyse über das Verhalten: Pfade, IPs, Typ.


Symptome den Regeltypen zuordnen

Oft verrät schon das Muster der Abfrage die Regelklasse:

Symptom Wahrscheinliche WAF-Regel Vorgehen
Abfrage nur auf /login Pfadbasierte Regel Challenge für diesen Pfad lösen
Abfrage nur von Rechenzentrums-IPs Bot-Score- oder IP-Reputationsregel Residential-Proxys oder Challenge lösen
Abfrage variiert je nach Land Länderregel Proxy im zulässigen Land oder lösen
Abfrage nach N Anfragen Rate-basierte Regel Anfragerate senken oder Challenge lösen
Immer JS-Challenge (nie Turnstile) JS-Challenge-Aktion (nicht Managed) cloudflare_challenge verwenden
403 ohne Challenge Block-Aktion (nicht lösbar) IP, Header oder Anfragemuster ändern

Ein Beispiel aus dem DACH-Alltag: Ein Preismonitoring-Dienst fragt von einem Hetzner-Server einen Shopware-Shop ab; die Kategorieseite läuft, doch /search liefert nach wenigen Dutzend Anfragen einen 503. Nur auf einem Pfad, erst nach mehreren Aufrufen: eine Rate-basierte Regel, keine Länderregel. Wer dabei Residential-Proxys nutzt, sollte bedenken: IP-Adressen gelten nach DSGVO als personenbezogene Daten – Datenflüsse und Rechtsgrundlage gehören vorab geprüft.


WAF-Challenges per API lösen

Steht der Typ fest, folgt die Methode: turnstile für Managed Challenges (Widget), cloudflare_challenge für die reine JavaScript-Prüfseite.

Strategie nach Challenge-Typ

import requests
import time

API_KEY = "YOUR_API_KEY"

def solve_cloudflare_challenge(url, challenge_type):
    """Solve Cloudflare challenge based on WAF rule action."""

    if challenge_type == "managed_challenge":
        # Managed Challenge typically renders as Turnstile
        method = "turnstile"
        sitekey = extract_turnstile_sitekey(url)
    elif challenge_type == "js_challenge":
        # JavaScript Challenge page
        method = "cloudflare_challenge"
        sitekey = "managed"
    else:
        raise ValueError(f"Unknown challenge type: {challenge_type}")

    submit = requests.post("https://ocr.captchaai.com/in.php", data={
        "key": API_KEY,
        "method": method,
        "sitekey": sitekey,
        "pageurl": 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")


def extract_turnstile_sitekey(url):
    """Fetch page and extract Turnstile sitekey."""
    import re
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
                      "AppleWebKit/537.36 Chrome/120.0.0.0",
    }
    response = requests.get(url, headers=headers, timeout=15)
    match = re.search(r'data-sitekey=["\']([0-9x][A-Za-z0-9_-]+)["\']', response.text)
    return match.group(1) if match else None

Das Token fügen Sie ins Formularfeld ein oder senden es mit der Folgeanfrage – mit derselben IP und demselben User-Agent, für die es galt.

Node.js

const axios = require("axios");

const API_KEY = "YOUR_API_KEY";

async function solveWAFChallenge(url, challengeType) {
  const method =
    challengeType === "js_challenge" ? "cloudflare_challenge" : "turnstile";
  const sitekey =
    challengeType === "js_challenge" ? "managed" : await extractSitekey(url);

  const submit = await axios.post("https://ocr.captchaai.com/in.php", null, {
    params: {
      key: API_KEY,
      method,
      sitekey,
      pageurl: url,
      json: 1,
    },
  });

  const taskId = submit.data.request;

  for (let i = 0; i < 60; i++) {
    await new Promise((r) => setTimeout(r, 5000));

    const result = await axios.get("https://ocr.captchaai.com/res.php", {
      params: { key: API_KEY, action: "get", id: taskId, json: 1 },
    });

    if (result.data.status === 1) {
      return result.data.request;
    }
  }

  throw new Error("Challenge solve timed out");
}

async function extractSitekey(url) {
  const response = await axios.get(url, {
    headers: {
      "User-Agent": "Mozilla/5.0 Chrome/120.0.0.0",
    },
  });
  const match = response.data.match(/data-sitekey=["']([0-9x][A-Za-z0-9_-]+)["']/);
  return match ? match[1] : null;
}

Regeländerungen erkennen und überwachen

WAF-Regeln sind nicht statisch: Betreiber justieren Schwellenwerte, ergänzen Pfade oder verschärfen Aktionen. Jede Änderung bedeutet für die Automatisierung anderes Verhalten:

Änderung Auswirkung auf die Automatisierung Woran Sie es erkennen
Regel hinzugefügt Auf bislang freien Pfaden erscheint eine neue Abfrage 503/403-Statuswechsel überwachen
Regel entfernt Die Abfrage verschwindet 200, wo vorher 503 stand
Aktion verschärft (Managed → Block) Lösbare Challenge wird harter Block 403 statt 503
Aktion gelockert (Block → Managed) Harter Block wird lösbare Challenge 503 mit Challenge-Seite
Schwellenwert geändert (Bot-Score 30 → 50) Mehr Anfragen werden geprüft Höhere Challenge-Häufigkeit
Pfadumfang geändert Andere URLs betroffen Neue Pfade liefern Abfragen

Änderungen automatisch überwachen

import requests
import time

def monitor_cloudflare_protection(urls, interval=3600):
    """Monitor protection changes across URLs."""
    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",
    }

    last_status = {}

    while True:
        for url in urls:
            try:
                response = requests.get(
                    url, headers=headers, timeout=15, allow_redirects=False
                )
                status = response.status_code
                has_challenge = status == 503 or "cf-turnstile" in response.text

                current = {"status": status, "challenge": has_challenge}
                previous = last_status.get(url)

                if previous and current != previous:
                    print(f"[CHANGE] {url}")
                    print(f"  Before: {previous}")
                    print(f"  After:  {current}")

                last_status[url] = current

            except requests.RequestException as e:
                print(f"[ERROR] {url}: {e}")

        time.sleep(interval)

So fällt der Wechsel von 200 auf 503 (neue Regel) oder 503 auf 403 (verschärfte Aktion) auf, bevor Batch-Läufe scheitern.


Häufige Fragen

Warum erscheint auf einmal ein CAPTCHA auf einer Route, die vorher lief?

Meist wurde eine neue WAF-Regel hinzugefügt oder ein Schwellenwert gesenkt. Ein Statuswechsel von 200 auf 503 auf einem freien Pfad ist das Signal – das erkennt das Monitoring-Skript oben.

turnstile oder cloudflare_challenge – welche Methode ist richtig?

turnstile, wenn die Seite ein Turnstile-Widget rendert (Marker cf-turnstile); cloudflare_challenge bei der reinen JavaScript-Prüfseite (Marker jschl bzw. challenge-platform).

Kann CaptchaAI einen 403-Block ohne Challenge lösen?

Nein. Ein harter Block liefert bewusst keine Challenge. Hier hilft nur ein anderes Anfragemuster – andere IP, saubere Header oder niedrigere Rate.

Reduzieren Residential-Proxys die Zahl der Abfragen?

In der Regel ja: Rechenzentrums-IPs erhalten häufiger niedrige Bot-Scores. Residential-Proxys mildern das, ersetzen aber keine Pfad- oder Länderregel – die greift unabhängig von der IP.

Nach dem Lösen lässt das cf_clearance-Cookie Folgeanfragen etwa 30 Minuten ohne Abfrage passieren. Es ist an IP und User-Agent gebunden – wechseln Sie eines, verfällt es.


Fazit

WAF-Regeln lösen CAPTCHA-Abfragen anhand konfigurierbarer Bedingungen aus: Bot-Score, Land, Pfad, Header oder Rate. Häufigste Aktion ist Managed Challenge – lösbar mit CaptchaAI über turnstile (Widget) oder cloudflare_challenge (JS-Prüfseite), je nachdem, was ausgeliefert wird. Ein harter 403-Block ist dagegen nicht lösbar; hier hilft nur, IP, Header oder Anfragemuster anzupassen.

Verwandte Artikel

Kommentare sind für diesen Artikel deaktiviert.