Введение
Cloudflare Turnstile всё чаще встречается на формах регистрации, логина и на страницах с дополнительной проверкой. Для обычного пользователя это почти незаметный шаг. Для автотестов, RPA и скриптов автоматизации ситуация другая: виджет нужно пройти программно, иначе сценарий останавливается.
В этой статье разберём практический путь через API: как собрать данные со страницы, создать задачу на решение Turnstile, получить токен и использовать его дальше в автоматизации. Фокус – на понятном flow «задача → результат» и на рабочих примерах, а не на рекламе конкретного сервиса.
Кому будет полезно: QA-инженерам, которые закрывают e2e-сценарии с защищёнными формами; разработчикам автоматизации и парсинга, где Turnstile блокирует поток; DevOps и тем, кто поддерживает внутренние боты и интеграционные проверки.
Что такое Cloudflare Turnstile
Классическая капча обычно просит выбрать картинки или ввести текст с изображения. Turnstile устроен иначе: это проверка Cloudflare, которая чаще работает в фоне и отдаёт браузеру токен после успешного прохождения. Для пользователя это может выглядеть как короткий виджет или почти незаметная проверка.
Где встречается Turnstile: формы логина и регистрации; формы обратной связи и подписки; страницы с challenge-защитой Cloudflare; сценарии, где сайт хочет отсеять простых ботов без тяжёлой картиночной капчи.
Для API-подхода со страницы обычно нужны два значения: websiteURL – адрес страницы, на которой показан виджет; и sitekey (websiteKey) – публичный ключ виджета Turnstile.
Важно заранее зафиксировать рамки статьи. Мы говорим о техническом API-подходе для автоматизации и тестирования. Это не инструкция «как обходить чужие сайты без разрешения». Используйте подход на своих стендах, в тестовых окружениях и там, где у вас есть право на такую автоматизацию.
Общая схема работы через API
Независимо от языка и фреймворка сценарий почти всегда одинаковый. Браузер или ваш скрипт не «рисует» капчу сам: он передаёт параметры задачи во внешний API, ждёт решение и получает токен, который сайт ожидает увидеть в форме или в запросе.
Базовый flow выглядит так:
Шаг 1. Получить API-ключ сервиса
Ключ нужен для авторизации запросов. Его обычно берут в личном кабинете провайдера API. В примерах ниже ключ будем обозначать как YOUR_API_KEY – в код его лучше передавать через переменные окружения, а не вшивать в репозиторий.
Шаг 2. Создать задачу (createTask)
В запросе указывают тип задачи (Turnstile), websiteURL и sitekey. API принимает задачу в работу и возвращает taskId – идентификатор, по которому потом забирают результат.
Шаг 3. Дождаться решения (getTaskResult / polling)
Решение занимает секунды. Пока статус processing, скрипт делает повторные запросы с небольшим интервалом. Когда задача готова, в ответе появляется токен Turnstile.
Шаг 4. Подставить токен в форму или запрос
Токен передают туда, куда его ждёт сайт: скрытое поле, callback виджета или параметр в HTTP-запросе. После этого сценарий продолжается – отправка формы, переход на следующий шаг, проверка в автотесте.
Отдельно стоит понимать роль окружения. Иногда достаточно proxyless-задачи (без своего прокси). В других случаях сайт чувствителен к IP, региону или «свежести» сессии – тогда нужен прокси, близкий к тому, с которого открывается страница. Для первых тестов на своём стенде чаще хватает простого варианта без прокси; тонкости разберём в примере с Playwright.
Подготовка данных со страницы
Перед вызовом API нужно собрать два обязательных параметра: адрес страницы и публичный ключ виджета. Ошибка на этом шаге – самая частая причина «бесконечного processing» или невалидного токена: задача создаётся, но данные не совпадают с тем, что ждёт сайт.
Как найти sitekey
Откройте страницу с Turnstile в браузере и DevTools (F12). Удобные места поиска:
-
Вкладка Elements / Inspector – найдите элемент виджета. Часто ключ лежит в атрибуте data-sitekey у контейнера Turnstile или в параметрах вызова turnstile.render(...).
-
Вкладка Network – отфильтруйте запросы по turnstile или challenges.cloudflare.com. В URL или payload иногда видно sitekey / k=...
-
Поиск по исходному HTML (Ctrl+F / Cmd+F) по строкам sitekey, data-sitekey, turnstile. Публичный ключ обычно выглядит как длинная строка символов; это не секрет сервера, его и задумано читать на клиенте.
Какой websiteURL указывать
В websiteURL передавайте тот адрес страницы, на которой реально показывается виджет: тот же origin и путь, с которого пользователь (или ваш автотест) инициирует проверку. Не подставляйте «похожий» домен, лендинг без виджета или URL после редиректа, если виджет был на предыдущем шаге – сайт может отклонить токен при проверке на сервере.
Если форма открывается в iframe, ориентируйтесь на URL фрейма, где живёт Turnstile, а не только на адрес родительской вкладки. Если виджет появляется после клика (модалка, второй шаг мастера), снимайте параметры уже с этого состояния страницы.
Что будет, если URL и key не совпадают
API может всё равно вернуть токен, но целевой сайт его не примет: форма «зависнет», вернётся ошибка валидации или снова покажет проверку. Поэтому сначала сверяйте пару websiteURL + sitekey на стабильном тестовом стенде, и только потом встраивайте вызов в длинный сценарий.
Мини-чеклист перед createTask
- Страница с виджетом открыта в том же сценарии, где будете подставлять токен
- sitekey скопирован целиком, без пробелов и обрезки
- websiteURL – полный HTTPS-адрес нужной страницы (схема + хост + путь)
- Понятно, куда потом вставлять токен (скрытое поле, callback, запрос)
- API-ключ сервиса доступен через переменную окружения, не в коде репозитория
Пример запроса createTask
Задачу на решение Turnstile создают методом createTask: HTTP POST с телом в JSON. Для CapMonster Cloud endpoint такой:
https://api.capmonster.cloud/createTask
Актуальные поля и типы задач лучше сверять с документацией – например, раздел Turnstile в CapMonster Cloud Docs и страница cloudflare turnstile solution. Ниже – минимальный рабочий каркас для обычного виджета без своего прокси.
Пример JSON-запроса
{
"clientKey": "YOUR_API_KEY",
"task": {
"type": "TurnstileTask",
"websiteURL": "https://example.com/login",
"websiteKey": "0x4AAAAAAABUYP0XeMJF0xoy"
}
}
Разбор полей
clientKey – ваш API-ключ сервиса. Не коммитьте его в git: передавайте из переменной окружения.
task.type – тип задачи. Для Turnstile в актуальной документации CapMonster Cloud указан TurnstileTask. Если на странице нужны дополнительные параметры challenge (data, pageAction, pageData и т.п.), их тоже снимают со страницы непосредственно перед созданием задачи: значения часто одноразовые.
task.websiteURL – адрес страницы с виджетом.
task.websiteKey – sitekey виджета Turnstile.
Что приходит в ответе
При успехе сервер возвращает идентификатор задачи. Упрощённо ответ выглядит так:
{
"errorId": 0,
"taskId": 407533072
}
taskId сохраните: по нему будете опрашивать getTaskResult. Если errorId не равен 0, в ответе будет код/описание ошибки – задачу ещё нельзя считать созданной.
Типичные ошибки на этом шаге
- Неверный или пустой clientKey – сразу ошибка авторизации.
- Опечатка в type или устаревшее имя типа задачи – API не примет payload; сверяйтесь с docs.
- Пустые websiteURL / websiteKey или sitekey с лишними пробелами / кавычками – задача может создаться, но решение будет бесполезным.
- Для сложных Cloudflare-сценариев не хватает динамических полей (сняли sitekey «вчера», а data/pageData уже другие) – параметры нужно извлекать прямо перед createTask.
- Content-Type не JSON или не POST – транспортный сбой до бизнес-логики.
Получение результата getTaskResult
После createTask решение не приходит мгновенно: сервис ставит задачу в очередь и обрабатывает её. Результат забирают методом getTaskResult – повторными запросами (polling), пока статус не станет ready либо не вернётся ошибка.
Endpoint:
https://api.capmonster.cloud/getTaskResult
Запрос – тоже JSON POST. В теле передают тот же clientKey и taskId из ответа createTask:
{
"clientKey": "YOUR_API_KEY",
"taskId": 407533072
}
Статусы и polling
Пока задача в работе, в ответе обычно status: processing. В этом случае скрипт ждёт (часто 1–3 секунды) и повторяет запрос. Не стоит долбить API десятки раз в секунду: достаточно спокойного цикла с паузой.
Когда решение готово, status становится ready, а в solution появляется токен. Для Turnstile в документации CapMonster Cloud типичное время ожидания – порядка нескольких секунд (зависит от нагрузки); закладывайте таймаут на весь цикл (например, 60–120 секунд), чтобы автотест не завис навечно.
Пример успешного ответа
{
"errorId": 0,
"status": "ready",
"solution": {
"userAgent": "userAgentPlaceholder",
"token": "0.iGX3xsyFCkbGePM3jP4P4khLo6TrLukt8ZzBvwu..."
}
}
Главное поле – solution.token: это строка, которую сайт ожидает как доказательство прохождения Turnstile. Иногда в ответе также есть userAgent – его имеет смысл использовать, если дальше запросы к сайту должны совпадать с окружением, в котором токен считался валидным (зависит от конкретной интеграции).
Куда вставлять токен
Варианты зависят от страницы:
- скрытое input-поле формы (часто name/id вроде cf-turnstile-response);
- callback виджета (turnstile.render с callback, куда передают token);
- параметр в вашем HTTP-запросе, если бэкенд принимает токен напрямую.
Важно успеть отправить форму, пока токен ещё свежий: если между получением solution и submit проходит слишком много времени, проверка на стороне сайта может не пройти. Поэтому в автотестах сразу после ready подставляют токен и продолжают сценарий, без длинных пауз.
На что смотреть при сбоях
- Вечный processing – проверьте taskId, лимиты ключа и не истёк ли общий таймаут цикла.
- errorId ≠ 0 на getTaskResult – читайте код ошибки в ответе; задачу, скорее всего, нужно создавать заново.
- ready есть, а сайт отклоняет токен – чаще проблема не в polling, а в паре URL/sitekey или в месте подстановки токена.
Практический пример в Playwright
Ниже – учебный каркас на Node.js + Playwright. Это не готовый production-скрипт «под любой сайт», а схема, которую вы подставите под свой стенд: свои селекторы формы, свой websiteURL и sitekey.
Идея сценария:
- открыть страницу с Turnstile;
- создать задачу через createTask;
- дождаться token через getTaskResult;
- записать токен в поле ответа виджета;
- отправить форму и проверить, что переход/ответ успешны.
Вспомогательные функции API
async function createTurnstileTask({ clientKey, websiteURL, websiteKey }) {
const res = await fetch("https://api.capmonster.cloud/createTask", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
clientKey,
task: {
type: "TurnstileTask",
websiteURL,
websiteKey,
},
}),
});
const data = await res.json();
if (data.errorId !== 0 || !data.taskId) {
throw new Error(`createTask failed: ${JSON.stringify(data)}`);
}
return data.taskId;
}
async function waitForToken({ clientKey, taskId, timeoutMs = 120000 }) {
const started = Date.now();
while (Date.now() - started < timeoutMs) {
const res = await fetch("https://api.capmonster.cloud/getTaskResult", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ clientKey, taskId }),
});
const data = await res.json();
if (data.errorId !== 0) {
throw new Error(`getTaskResult failed: ${JSON.stringify(data)}`);
}
if (data.status === "ready") {
return data.solution.token;
}
await new Promise((r) => setTimeout(r, 2000));
}
throw new Error("Turnstile solve timeout");
}
createTurnstileTask отправляет минимальный payload и возвращает taskId. waitForToken делает polling раз в 2 секунды, пока не появится solution.token или не истечёт таймаут.
Сценарий Playwright
import { chromium } from "playwright";
const CLIENT_KEY = process.env.CAPMONSTER_CLIENT_KEY;
const PAGE_URL = "https://example.com/login"; // ваш стенд
const SITE_KEY = "0x4AAAA..."; // sitekey со страницы
const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto(PAGE_URL, { waitUntil: "domcontentloaded" });
// дождитесь появления виджета / формы
await page.waitForSelector('[name="cf-turnstile-response"], textarea[name="cf-turnstile-response"]', {
state: "attached",
timeout: 30000,
}).catch(() => {});
const taskId = await createTurnstileTask({
clientKey: CLIENT_KEY,
websiteURL: PAGE_URL,
websiteKey: SITE_KEY,
});
const token = await waitForToken({ clientKey: CLIENT_KEY, taskId });
// подстановка токена: селектор зависит от вёрстки сайта
await page.evaluate((value) => {
const input =
document.querySelector('[name="cf-turnstile-response"]') ||
document.querySelector('textarea[name="cf-turnstile-response"]');
if (!input) throw new Error("Turnstile response field not found");
input.value = value;
input.dispatchEvent(new Event("input", { bubbles: true }));
input.dispatchEvent(new Event("change", { bubbles: true }));
}, token);
await page.locator('button[type="submit"]').click();
await page.waitForURL(/dashboard|success|home/i, { timeout: 30000 });
await browser.close();
Что здесь происходит по шагам
- chromium.launch / page.goto – открываем ту же страницу, для которой собрали websiteURL.
- Опциональный waitForSelector – ждём поле ответа Turnstile (имя поля на реальном сайте может отличаться).
- createTurnstileTask + waitForToken – получаем свежий token.
- page.evaluate – записываем token в скрытое поле и шлём события input/change, чтобы фронтенд «увидел» изменение.
- click по submit и waitForURL – проверяем, что сценарий прошёл дальше. В вашем тесте вместо URL может быть ожидание текста, API-ответа или исчезновения ошибки формы.
Адаптация под реальный сайт
Если виджет работает через callback, токен иногда нужно передать не в input, а вызвать сохранённый callback или выполнить turnstile.getResponse() после программной установки. Если форма в iframe – переключайтесь на frameLocator. Если сайт сверяет User-Agent, используйте userAgent из solution (когда он приходит) при создании контекста браузера.
Ключ CLIENT_KEY храните только в переменных окружения. Для статьи и демо достаточно одного успешного прогона на своём тестовом стенде.
Частые ошибки и как их избежать
Большинство сбоев в связке «страница → createTask → token → submit» повторяются. Ниже – короткий разбор, чтобы быстрее понять, на каком шаге ломается сценарий.
Неверный sitekey или websiteURL
Симптом: API отдаёт token, но форма снова показывает проверку или сервер отвечает ошибкой валидации. Проверьте, что ключ снят с той же страницы (того же iframe/шага мастера), а URL – полный адрес с виджетом, без «похожего» домена и без лишних редиректов.
Токен протух до отправки формы
Симптом: сразу после ready всё хорошо в логах, но submit через минуту уже не проходит. Не держите длинные паузы между getTaskResult и отправкой: подставили token → сразу продолжили сценарий. При необходимости создайте задачу заново.
Слишком редкий или слишком частый polling
Редкий polling (раз в 15–30 секунд) удлиняет тест без пользы. Слишком частый (десятки запросов в секунду) нагружает API и не ускоряет решение. Обычно достаточно интервала 1–3 секунды и общего таймаута на цикл.
Ожидание «мгновенного» решения
Turnstile через API – это асинхронная задача. В автотесте всегда нужен цикл ожидания ready (или обёртка вроде waitForToken), а не один вызов getTaskResult сразу после createTask.
Путаница Turnstile и Cloudflare Challenge
Обычный виджет Turnstile на форме и более сложный challenge на защите страницы – разные сценарии. Для challenge часто нужны дополнительные динамические параметры (их снимают со страницы непосредственно перед задачей). Если минимальный TurnstileTask с URL и sitekey не помогает, сверьтесь с актуальным разделом docs по Turnstile/Challenge и набору полей для вашего случая.
Не то место подстановки token
Скрытое поле может называться иначе, чем cf-turnstile-response; иногда нужен callback виджета, а не value у input. Смотрите, как страница реально читает ответ: в HTML, в обработчике submit или в сетевом запросе после клика.
Когда API не нужен
API-решение Turnstile – инструмент для повторяемой автоматизации. В ряде случаев он избыточен, и проще обойтись без него.
Ручная проверка одной формы
Если нужно один раз пройти логин или убедиться, что виджет на стенде живой, быстрее сделать это руками в браузере. Подключать createTask / polling ради единичного клика обычно не стоит.
Тестовый ключ или стенд без защиты
На staging часто отключают Turnstile, подставляют тестовые ключи Cloudflare или обходят проверку конфигурацией окружения. Тогда автотест проверяет бизнес-логику формы, а не интеграцию с капчей – и это нормальная стратегия для большей части pipeline.
Этичная оговорка
Используйте API-подход на своих системах, в тестовых контурах и там, где у вас есть явное право на такую автоматизацию. Статья описывает технический механизм для разработки и QA, а не способ обхода чужих ограничений без разрешения владельца сервиса.
Заключение
Cloudflare Turnstile в автоматизации удобно проходить через один и тот же API-flow: снять websiteURL и sitekey со страницы, создать задачу, дождаться token и сразу подставить его в форму или запрос. На практике важны не «магические» настройки, а точность параметров и короткий путь от ready до submit.
Checklist из 5 пунктов
- Пара websiteURL + sitekey снята с той же страницы (того же шага/iframe), где будете отправлять форму.
- createTask уходит с актуальным type и вашим clientKey из переменной окружения.
- getTaskResult крутится в цикле с паузой 1–3 секунды и разумным таймаутом, пока не будет status: ready.
- solution.token подставляется в то поле или callback, которое реально читает фронтенд, без длинной паузы до submit.
- Сценарий проверен на своём стенде: форма уходит дальше, ошибки валидации капчи нет.
Дальше имеет смысл держать под рукой официальную документацию по типу задачи Turnstile и страницу продукта cloudflare turnstile solution на capmonster.cloud – как первоисточник полей API, если что-то изменится в параметрах задачи. Удачных стабильных e2e и меньше ручных кликов по виджету.
Комментарии