Пользователь на одном сайте с Cloudflare Turnstile видит чекбокс «Verify you are human», на другом — вообще ничего. Разница в режиме виджета: управляемом (Cloudflare сама решает, что показать), неинтерактивном (только фоновая проверка, интерфейс не появляется никогда) и невидимом (контейнер вообще не рендерится). Для автоматизации это вопрос детектирования: сначала нужно понять режим страницы, и только потом извлекать sitekey.
Все три режима отдают один и тот же токен
cf-turnstile-responseи решаются одним методом CaptchaAI —turnstile. Разница только в том, что видно пользователю и как найтиsitekeyв разметке.
Быстрый ориентир: как понять, какой режим перед вами
Не открывая документацию, режим можно определить за три шага:
- Есть видимый чекбокс, который иногда пропадает без клика — это управляемый режим.
- Виден только спиннер, а чекбокса не появляется никогда — это неинтерактивный режим.
- На странице нет вообще ни одного элемента, привязанного к виджету — это невидимый режим.
Управляемый режим: Cloudflare решает за пользователя
В управляемом режиме — по умолчанию — Cloudflare сама подбирает уровень проверки под посетителя. Основная масса трафика проходит без интерфейса. Подозрительные сигналы получают чекбокс, совсем плохая репутация — усложнённую проверку.
Стандартная разметка выглядит так:
<!-- 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>
Что видит автоматизация в зависимости от репутации источника:
- Высокое доверие — проходит невидимо, интерфейса нет.
- Среднее доверие — чекбокс: «нажмите для проверки».
- Низкое доверие — интерактивная проверка или блок.
Для скриптов управляемый режим самый непредсказуемый: один sitekey может то показывать чекбокс, то проходить незаметно — в зависимости от сигналов браузера. Отличить его в HTML можно так:
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
Неинтерактивный режим: проверка без интерфейса
Неинтерактивный режим никогда не показывает чекбокс или другой кликабельный элемент. Виден только спиннер, пока в фоне идёт proof-of-work. Если проверка не проходит, виджет сразу сообщает об ошибке — эскалации до чекбокса не будет.
<!-- Non-interactive mode -->
<div class="cf-turnstile"
data-sitekey="0x4AAAAAAAC3DHQhMMQ_Rxrg"
data-appearance="interaction-only">
</div>
Тот же режим можно включить через JavaScript API:
turnstile.render('#turnstile-container', {
sitekey: '0x4AAAAAAAC3DHQhMMQ_Rxrg',
appearance: 'interaction-only',
callback: function(token) {
document.getElementById('cf-turnstile-response').value = token;
},
});
По шагам это выглядит так:
Page loads → Widget initializes
↓
Background proof-of-work runs
↓
Success → Token generated (no visible UI)
OR
Failure → Widget reports error (no fallback to checkbox)
Где чаще всего встречается неинтерактивный режим:
- Формы комментариев и виджеты обратной связи.
- Подписка на рассылку.
- Действия с низкой ценой ошибки, где трение нужно свести к минимуму.
- API-эндпоинты с защитой на стороне браузера.
Невидимый режим: без единого визуального признака
Невидимый режим — буквально невидим: в вьюпорте не появляется ни один элемент, привязанный к виджету. Проверка запускается при загрузке страницы (или по программному триггеру) и отдаёт токен без какой-либо визуальной подсказки.
<!-- Invisible mode — container is hidden -->
<div id="turnstile-invisible"
class="cf-turnstile"
data-sitekey="0x4AAAAAAAC3DHQhMMQ_Rxrg"
data-size="invisible">
</div>
Или полностью программно:
// 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');
},
});
У невидимого Cloudflare Turnstile нет видимых размеров контейнера, поэтому детектор должен опираться на признаки в разметке и сетевых запросах, а не на визуальный поиск элемента:
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 вне зависимости от режима
Какой бы режим ни стоял на странице, для решения нужен один и тот же параметр — sitekey. Достать его можно тремя способами: из атрибута data-sitekey, из вызова turnstile.render() в JS или из конфигурационного объекта:
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
Как решить любой режим через API CaptchaAI
Режим виджета не влияет на вызов API — все три решаются одной функцией с методом turnstile. Отправьте sitekey и pageurl, получите ID задачи и опросите res.php:
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://staging.example.com/qa-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://staging.example.com/qa-login")
.then((token) => console.log("Token:", token.substring(0, 50)));
Сравнение режимов в одной таблице
Сводка для быстрой сверки, когда все три режима уже разобраны по отдельности:
| Особенность | Управляемый | Неинтерактивный | Невидимый |
|---|---|---|---|
| Виджет виден? | Иногда | Никогда (только спиннер) | Никогда |
| Нужен контейнер в DOM? | Да | Да | Да (скрытый) |
| Требуется клик пользователя? | Иногда (чекбокс) | Нет | Нет |
| Проверка proof-of-work? | Да (может усложниться) | Да (всегда) | Да (всегда) |
| Резервный чекбокс при сбое? | Да | Нет — сразу ошибка | Нет — сразу ошибка |
| Токен | cf-turnstile-response |
cf-turnstile-response |
cf-turnstile-response |
| Метод CaptchaAI | turnstile |
turnstile |
turnstile |
| Где чаще встречается | Вход, регистрация | Формы с низким трением | Фоновая проверка |
Типичные проблемы при работе с Cloudflare Turnstile
- Токен верный, но форма его не принимает —
sitekeyне совпадает с тем, что реально отрендерился (на странице другой виджет). Проверьтеsitekeyиз JS-рендера, а не только из статического HTML. - Виджет не находится в HTML — невидимый режим монтируется уже после первого рендера страницы. Дождитесь полной загрузки и проверьте XHR-ответы.
- На странице несколько виджетов Turnstile — у разных форм разные
sitekey. Сопоставляйтеsitekeyс конкретной формой, а не берите первый попавшийся. data-size="compact"путает детектор — compact это размер, а не режим; по умолчанию используется управляемый режим.- Присутствует
data-action— это тег для аналитики, а не признак режима; передавайте action в решение только если он нужен для валидации. - Токен просрочен к моменту отправки — токены Cloudflare Turnstile действуют 300 секунд. Решайте задачу непосредственно перед отправкой формы.
Частые вопросы
Меняется ли вызов API CaptchaAI в зависимости от режима Turnstile?
Нет. Управляемый, неинтерактивный и невидимый режимы решаются одним и тем же методом turnstile. Единственные обязательные параметры — sitekey и pageurl, формат токена и порядок опроса res.php от режима не зависят.
Сколько потоков тарифа нужно, чтобы решать managed-режим при пиковой нагрузке?
Отталкивайтесь от пика одновременных задач, а не от среднего трафика: managed-режим резко наращивает долю чекбоксов при всплеске подозрительных сигналов. Для staging с пиком в 30–40 параллельных решений подходит ADVANCE ($90/мес, 50 потоков); для небольших QA-прогонов хватит STANDARD ($30/мес, 15 потоков).
Почему невидимый виджет иногда не находится в HTML?
Чаще всего invisible-контейнер добавляется в DOM программно, уже после первичного рендера, или скрыт стилями без явного data-size="invisible" в разметке. Проверяйте не только HTML, но и вызовы turnstile.render() и запросы к challenges.cloudflare.com.
Может ли один и тот же sitekey переключаться между режимами?
Да — сайт может держать управляемый режим по умолчанию и переводить часть страниц на неинтерактивный, оставляя sitekey прежним. Определяйте режим заново при каждой навигации, а не кэшируйте результат на весь сеанс.
Что делать, если Turnstile решён, а форма всё равно отклоняет токен?
Проверьте, что в форму подставлен тот же sitekey, что отрендерился в браузере — при нескольких виджетах или асинхронной загрузке легко решить не тот. Также убедитесь, что токен отправляется в течение 300 секунд.
Коротко о главном
Три режима виджета Cloudflare Turnstile — управляемый, неинтерактивный и невидимый — по-разному ведут себя в интерфейсе, но всегда отдают один и тот же токен cf-turnstile-response. Для автоматизации все три решаются одинаково через метод turnstile в решателе Cloudflare Turnstile от CaptchaAI, с высокой долей успешных решений на поддерживаемых типах. Главное отличие для разработчика — не решение, а детектирование: managed-режим обычно виден в статическом HTML, а invisible требует более глубокого анализа страницы, чтобы найти sitekey.
Похожие статьи
Подробнее по смежным темам: как отличить страницу с Cloudflare Challenge от Cloudflare Turnstile, GeeTest против Cloudflare Turnstile и что делать с ошибкой 403 в Cloudflare Turnstile после починки токена.