Die wichtigste Erkenntnis vorweg: Turnstile kennt drei Widget-Modi, aber alle drei liefern dasselbe cf-turnstile-response-Token und werden mit demselben Aufruf gelöst – method=turnstile, Sitekey plus Page-URL. Der Modus ändert nicht, wie Sie lösen, sondern nur, wie schwer er im HTML zu finden ist: Der Managed-Modus zeigt oft sichtbares Markup, der Non-Interactive-Modus nur einen Spinner, der Invisible-Modus gar keinen Container. Wer diese Muster kennt, weiß auf jeder Seite, wo der Sitekey steckt.
Welchen Modus haben Sie vor sich?
So ordnen Sie ein Turnstile-Widget in wenigen Sekunden dem richtigen Modus zu:
- Sichtbares Kontrollkästchen, das nur manchmal erscheint? Das ist der Managed-Modus.
- Nur ein Spinner, aber nie ein Kontrollkästchen? Dann läuft der Non-Interactive-Modus.
- Kein sichtbarer Container, nur Skript und Antwortfeld im HTML? Dann ist der Invisible-Modus aktiv.
Schneller geht es über das gesetzte Attribut:
| HTML-Signal | Modus |
|---|---|
data-appearance="interaction-only" |
Non-Interactive |
data-size="invisible" |
Invisible |
| keine Modus-Angabe | Managed (Standard) |
Die drei Modi im Überblick
Token-Feld und CaptchaAI-Methode sind überall identisch; anders sind nur Sichtbarkeit, Interaktion und Fehlerverhalten.
| Funktion | Verwaltet | Nicht interaktiv | Unsichtbar |
|---|---|---|---|
| Widget sichtbar? | Manchmal | Nie (nur Spinner) | Niemals |
| Containerelement erforderlich? | Ja | Ja | Ja (versteckt) |
| Benutzerinteraktion erforderlich? | Manchmal (Kontrollkästchen) | Nein | Nein |
| Proof-of-Work-Abfrage? | Ja (kann eskalieren) | Ja (immer) | Ja (immer) |
| Interaktives Kontrollkästchen-Fallback? | Ja | Nein (schlägt stattdessen fehl) | Nein (schlägt stattdessen fehl) |
| Token-Ausgabe | cf-turnstile-response |
cf-turnstile-response |
cf-turnstile-response |
| CaptchaAI-Methode | turnstile |
turnstile |
turnstile |
| Empfohlen für | Login, Registrierung | Reibungsarme Formulare | Hintergrundprüfung |
Managed-Modus: Cloudflare entscheidet (Standard)
Der Managed-Modus ist die Voreinstellung: Cloudflare legt die Prüfstufe pro Besucher fest und rendert das Widget je nach Reputation unterschiedlich:
- Hohes Vertrauen: unsichtbarer Pass, keine sichtbare Benutzeroberfläche
- Mittleres Vertrauen: Kontrollkästchen-Widget zum Bestätigen
- Geringes Vertrauen: interaktive Abfrage oder Blockierung
Für die Automatisierung ist das der häufigste und variabelste Fall: Derselbe Sitekey zeigt im Test mal ein Kontrollkästchen, mal gar nichts – die Stufe entscheidet sich zur Laufzeit, nicht im Markup.
Einbindung im HTML
<!-- Managed mode (default) -->
<div class="cf-turnstile"
data-sitekey="0x4AAAAAAAC3DHQhMMQ_Rxrg"
data-theme="light">
</div>
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
Managed-Modus im HTML erkennen
Der Managed-Modus hat kein eigenes Attribut – er ist die Abwesenheit einer expliziten Modus-Angabe. Prüfen Sie also, ob cf-turnstile vorhanden ist und keine Modus-Option gesetzt wurde:
def is_managed_mode(html):
"""Check if Turnstile is using managed mode (default)."""
# Managed mode is the default — no explicit mode attribute
has_turnstile = "cf-turnstile" in html
has_explicit_mode = 'data-appearance="interaction-only"' in html or \
'data-appearance="always"' in html or \
'appearance: "interaction-only"' in html
return has_turnstile and not has_explicit_mode
Non-Interactive-Modus: nur Proof-of-Work
Im Non-Interactive-Modus erscheint nie ein Kontrollkästchen. Turnstile rechnet im Hintergrund eine Proof-of-Work-Abfrage und zeigt nur einen Lade-Spinner.
Lässt sie sich nicht ohne Interaktion abschließen, schlägt sie fehl – anders als der Managed-Modus eskaliert sie nicht zum Kontrollkästchen.
Einbindung
<!-- Non-interactive mode -->
<div class="cf-turnstile"
data-sitekey="0x4AAAAAAAC3DHQhMMQ_Rxrg"
data-appearance="interaction-only">
</div>
Oder über die JavaScript-API:
turnstile.render('#turnstile-container', {
sitekey: '0x4AAAAAAAC3DHQhMMQ_Rxrg',
appearance: 'interaction-only',
callback: function(token) {
document.getElementById('cf-turnstile-response').value = token;
},
});
Ablauf
Page loads → Widget initializes
↓
Background proof-of-work runs
↓
Success → Token generated (no visible UI)
OR
Failure → Widget reports error (no fallback to checkbox)
Wo der Non-Interactive-Modus üblich ist
- Kommentarformulare und Feedback-Widgets
- Newsletter-Anmeldungen
- Aktionen mit geringem Wert und minimaler Reibung
- API-Endpunkte mit browserseitigem Schutz
Beispiel: Newsletter mit Double-Opt-In
Newsletter-Anmeldungen mit Double-Opt-In – im DACH-Raum rechtlicher Standard – setzen häufig auf den Non-Interactive-Modus. Für Ihre QA heißt das: kein Kontrollkästchen zum Anklicken – Sie lösen das Token per API und tragen es vor dem Absenden in cf-turnstile-response ein.
Invisible-Modus: ganz ohne sichtbaren Container
Der Invisible-Modus ist tatsächlich unsichtbar – im Ansichtsfenster erscheint kein Containerelement.
Das Widget läuft beim Laden der Seite oder per Auslöser und erzeugt ein Token ohne sichtbare Anzeige.
<!-- Invisible mode — container is hidden -->
<div id="turnstile-invisible"
class="cf-turnstile"
data-sitekey="0x4AAAAAAAC3DHQhMMQ_Rxrg"
data-size="invisible">
</div>
Oder komplett per JavaScript:
// Programmatic invisible Turnstile
turnstile.render('#hidden-container', {
sitekey: '0x4AAAAAAAC3DHQhMMQ_Rxrg',
size: 'invisible',
callback: function(token) {
// Token ready — submit form automatically
submitForm(token);
},
'error-callback': function() {
// Challenge failed
console.error('Invisible Turnstile failed');
},
});
Warum die Erkennung hier schwieriger ist
Weil der Container keine sichtbaren Abmessungen hat, greift die reine Sicht-Erkennung nicht. Nützlicher ist ein Bündel von Signalen, aus denen Sie eine Konfidenz ableiten:
- das geladene Turnstile-Skript von
challenges.cloudflare.com - das Attribut
data-size="invisible" - ein
turnstile.render-Aufruf im JavaScript - das Antwortfeld
cf-turnstile-response
import re
def detect_invisible_turnstile(html):
"""Detect invisible Turnstile on a page."""
indicators = {
"script_loaded": "challenges.cloudflare.com/turnstile" in html,
"size_invisible": 'data-size="invisible"' in html or
"size: 'invisible'" in html or
'size: "invisible"' in html,
"api_render_call": "turnstile.render" in html,
"response_field": "cf-turnstile-response" in html,
}
if indicators["script_loaded"] and indicators["size_invisible"]:
return {"mode": "invisible", "confidence": "high"}
elif indicators["script_loaded"] and indicators["api_render_call"]:
return {"mode": "invisible_or_programmatic", "confidence": "medium"}
elif indicators["response_field"]:
return {"mode": "turnstile_present", "confidence": "low"}
return {"mode": "none", "confidence": "high"}
Sitekey aus jedem Modus extrahieren
Unabhängig vom Modus brauchen Sie zum Lösen nur den Sitekey. Er kann an drei Stellen im HTML stehen:
- im
data-sitekey-Attribut des Containers - in einem JavaScript-
render-Aufruf - in einem Konfigurationsobjekt der Seite
Die folgende Funktion deckt alle drei Fundstellen ab:
import re
def extract_turnstile_sitekey(html):
"""Extract Turnstile sitekey from page HTML (works for all modes)."""
# Pattern 1: data-sitekey attribute in HTML
match = re.search(r'data-sitekey=["\']([0-9x][A-Za-z0-9_-]+)["\']', html)
if match:
return match.group(1)
# Pattern 2: JavaScript render call
match = re.search(r"sitekey:\s*['\"]([0-9x][A-Za-z0-9_-]+)['\"]", html)
if match:
return match.group(1)
# Pattern 3: Turnstile config object
match = re.search(r"siteKey['\"]?\s*[:=]\s*['\"]([0-9x][A-Za-z0-9_-]+)['\"]", html)
if match:
return match.group(1)
return None
Alle drei Modi mit CaptchaAI lösen
Hier zahlt sich die Eingangsbeobachtung aus. Sie übergeben nur zwei Werte:
- den Sitekey aus dem HTML
- die Page-URL der Seite mit dem Widget
Anschließend fragen Sie per Polling ab und erhalten dasselbe Token – egal welcher Modus.
Python
import requests
import time
API_KEY = "YOUR_API_KEY"
def solve_turnstile(sitekey, page_url):
"""Solve any Turnstile mode — managed, non-interactive, or invisible."""
submit = requests.post("https://ocr.captchaai.com/in.php", data={
"key": API_KEY,
"method": "turnstile",
"sitekey": sitekey,
"pageurl": page_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("Turnstile solve timed out")
# Use with any mode
token = solve_turnstile("0x4AAAAAAAC3DHQhMMQ_Rxrg", "https://example.com/login")
print(f"Token: {token[:50]}...")
Node.js
const axios = require("axios");
const API_KEY = "YOUR_API_KEY";
async function solveTurnstile(sitekey, pageUrl) {
const submit = await axios.post("https://ocr.captchaai.com/in.php", null, {
params: {
key: API_KEY,
method: "turnstile",
sitekey,
pageurl: pageUrl,
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("Turnstile solve timed out");
}
// Same function works for all Turnstile modes
solveTurnstile("0x4AAAAAAAC3DHQhMMQ_Rxrg", "https://example.com/login")
.then((token) => console.log("Token:", token.substring(0, 50)));
DSGVO-Hinweis: Wenn Sie Page-URLs oder Tokens protokollieren, beachten Sie den Datenschutz – URLs und Proxy-IP-Adressen können personenbezogene Daten sein.
Fehlerbehebung
| Symptom | Ursache | Beheben |
|---|---|---|
| Token gültig, aber Formular lehnt es ab | Falscher Sitekey (anders als sichtbares Widget) | Suchen Sie nach einem mit JavaScript gerenderten Sitekey |
| Widget nicht in HTML gefunden | Invisible-Modus, der erst nach dem ersten Rendern lädt | Warten Sie, bis die Seite vollständig geladen ist, und prüfen Sie die XHR-Antworten |
| Mehrere Turnstile-Widgets auf der Seite | Verschiedene Sitekeys für verschiedene Formulare | Ordnen Sie den Sitekey dem konkreten Formular zu |
data-size="compact" verwirrt die Erkennung |
Compact ist eine Größenvariante, kein Modus | Compact nutzt standardmäßig den Managed-Modus |
data-action-Attribut vorhanden |
Aktions-Tag für Analysen, kein Modus | Aktion in die Lösung einbeziehen, falls für die Validierung nötig |
| Token läuft vor der Übermittlung ab | Turnstile-Token laufen nach 300 Sekunden ab | Unmittelbar vor dem Absenden lösen |
Häufige Fragen
Was unterscheidet data-appearance="interaction-only" von data-size="invisible"?
Die beiden Attribute steuern verschiedene Dinge:
data-appearance="interaction-only"schaltet den Non-Interactive-Modus – Spinner, aber nie ein Kontrollkästchen.data-size="invisible"schaltet den Invisible-Modus – der Container hat keine sichtbaren Abmessungen.
Kurz gesagt: das eine steuert die Interaktion, das andere die Größe.
Warum schlägt der Non-Interactive-Modus fehl, statt ein Kontrollkästchen zu zeigen?
Weil das genau seine Aufgabe ist:
- Der Managed-Modus darf zum Kontrollkästchen eskalieren.
- Der Non-Interactive-Modus nicht – scheitert die Proof-of-Work-Abfrage ohne Interaktion, meldet das Widget einen Fehler, ohne Fallback.
Wie lange ist ein Turnstile-Token gültig?
Turnstile-Token laufen nach 300 Sekunden ab.
Lösen Sie deshalb erst kurz vor dem Absenden, nicht Minuten im Voraus – ein abgelaufenes Token weist der Server ab, obwohl es formal korrekt aussieht.
Muss ich das data-action-Attribut beim Lösen mitgeben?
Meist nicht. data-action ist ein Label für die serverseitige Auswertung und ändert weder Modus noch Token-Format.
Nur wenn die Zielseite die Aktion bei der Validierung erwartet, nehmen Sie sie in Ihren Lösungsablauf auf.
Fazit
Die drei Turnstile-Modi steuern die Nutzererfahrung, liefern aber dasselbe cf-turnstile-response-Token und werden identisch gelöst – mit dem Turnstile-Solver von CaptchaAI, mit hoher Erfolgsquote auf allen unterstützten Modi. Der Unterschied liegt allein in der Erkennung:
- Managed: sichtbares HTML, am leichtesten zu finden
- Non-Interactive: nur ein Spinner, erkennbar am
data-appearance-Attribut - Invisible: kein Container, erfordert eine tiefere Seitenanalyse