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.
Wie lange gilt das cf_clearance-Cookie nach dem Lösen?
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.