← Cloudflare Turnstile / turnstile / get-started
Встраивание виджета
Как добавить виджет Turnstile на страницу с помощью неявной или явной отрисовки.
Turnstile предлагает два способа добавить виджет на страницу. Неявная отрисовка при загрузке страницы автоматически находит в вашем HTML контейнеры виджетов. Явный рендеринг даёт программный контроль и позволяет создавать виджеты в любой момент средствами JavaScript. Неявный рендеринг подходит для статических страниц, где формы существуют уже при загрузке. Явный рендеринг подходит для динамического содержимого и одностраничных приложений (SPA), где формы создаются после первоначальной загрузки страницы.
| Возможность | Неявная отрисовка | Явный рендеринг |
|---|---|---|
| Простота настройки | Простой, минимальный код | Требуется дополнительный код JavaScript |
| Контроль над временем запуска | Отрисовывается автоматически при загрузке страницы | Полный контроль над моментом отрисовки |
| Сценарии использования | Статическое содержимое | Динамический или интерактивный контент |
| Настройка | Ограничено атрибутами HTML | Широкие возможности через JavaScript API |
Предварительные требования
Перед началом работы вам понадобится:
- Аккаунт Cloudflare
- Виджет Turnstile с sitekey
- Возможность редактировать HTML вашего сайта
- Базовые знания HTML и JavaScript
Процесс
- Загрузка страницы: скрипт Turnstile загружается и ищет нужные элементы либо ждёт программного вызова.
- Рендеринг виджета: виджеты создаются и начинают выполнять проверки.
- Генерация токена: после прохождения проверки создаётся токен.
- Интеграция с формой: токен передаётся через функции обратного вызова или скрытые поля формы.
- Проверка на сервере: ваш сервер получает токен и проверяет его через Siteverify API.
Неявная отрисовка
При неявной отрисовке Turnstile автоматически ищет в вашем HTML элементы с классом cf-turnstile в качестве класса и отрисовывает виджеты без дополнительного кода на JavaScript. Такая настройка идеально подходит для статических страниц, где виджет должен загружаться сразу вместе со страницей.
Сценарии использования
Cloudflare рекомендует применять неявный рендеринг в следующих случаях:
- У вас простая реализация и нужна быстрая интеграция.
- У вас статические сайты с несложными формами.
- Вы хотите, чтобы виджеты появлялись сразу при загрузке страницы.
- Вам не нужно программно управлять виджетом.
Внедрение
1. Добавьте скрипт Turnstile
Подключение скрипта Turnstile: добавьте Turnstile JavaScript API в HTML-файл внутри <head> или непосредственно перед закрывающим </body> тег.
<script
src="https://challenges.cloudflare.com/turnstile/v0/api.js"
async
defer
></script>2. (Необязательно) Оптимизируйте производительность с помощью resource hints
Добавьте подсказки ресурсов (resource hints), чтобы браузер заранее устанавливал соединение с серверами Cloudflare и страница загружалась быстрее. Разместите этот <link> в HTML-раздел <head> перед скриптом Turnstile.
<link rel="preconnect" href="https://challenges.cloudflare.com" />3. Добавьте элементы виджетов
Разместите контейнеры виджетов там, где на сайте должны появляться проверки.
<div class="cf-turnstile" data-sitekey="<YOUR-SITE-KEY>"></div>4. Настройте с помощью атрибутов data
Настройка виджетов с помощью атрибутов data. Вставьте div в том месте, где должен появиться виджет.
<div
class="cf-turnstile"
data-sitekey="<YOUR-SITE-KEY>"
data-theme="light"
data-size="normal"
data-callback="onSuccess"
></div>После прохождения проверки токен передаётся в обратный вызов success. Этот токен нужно проверить через наш Конечная точка Siteverify.
Полные примеры неявного рендеринга по сценариям
Простая форма входа
Turnstile часто применяют для защиты форм на сайтах, например форм входа или обратной связи. Виджет можно встроить в <form> тег.
<!DOCTYPE html>
<html>
<head>
<title>Login Form</title>
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
</head>
<body>
<form action="/login" method="POST">
<input type="text" name="username" placeholder="Username" autocomplete="username" required />
<input type="password" name="password" placeholder="Password" autocomplete="current-password" required />
<!-- Turnstile widget with basic configuration -->
<div class="cf-turnstile" data-sitekey="<YOUR-SITE-KEY>"></div>
<button type="submit">Log in</button>
</form>
</body>
</html>Скрытое поле ввода с именем cf-turnstile-response добавляется и отправляется на сервер вместе с остальными полями.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Implicit Rendering with Cloudflare Turnstile</title>
<script
src="https://challenges.cloudflare.com/turnstile/v0/api.js"
async
defer
></script>
</head>
<body>
<h1>Contact Us</h1>
<form action="/submit" method="POST">
<label for="name">Name:</label><br />
<input type="text" id="name" name="name" required /><br />
<label for="email">Email:</label><br />
<input type="email" id="email" name="email" required /><br />
<!-- Turnstile Widget -->
<div class="cf-turnstile" data-sitekey="<YOUR-SITE-KEY>"></div>
<br />
<button type="submit">Submit</button>
</form>
</body>
</html>Расширенная форма с обратными вызовами
<form action="/contact" method="POST" id="contact-form">
<input type="email" name="email" placeholder="Email" required />
<textarea name="message" placeholder="Message" required></textarea>
<!-- Widget with callbacks and custom configuration -->
<div
class="cf-turnstile"
data-sitekey="<YOUR-SITE-KEY>"
data-theme="auto"
data-size="flexible"
data-callback="onTurnstileSuccess"
data-error-callback="onTurnstileError"
data-expired-callback="onTurnstileExpired"
></div>
<button type="submit" id="submit-btn" disabled>Send Message</button>
</form>
<script>
function onTurnstileSuccess(token) {
console.log("Turnstile success:", token);
document.getElementById("submit-btn").disabled = false;
}
function onTurnstileError(errorCode) {
console.error("Turnstile error:", errorCode);
document.getElementById("submit-btn").disabled = true;
}
function onTurnstileExpired() {
console.warn("Turnstile token expired");
document.getElementById("submit-btn").disabled = true;
}
</script>Несколько виджетов с разными настройками
<!-- Compact widget for newsletter signup -->
<form action="/newsletter" method="POST">
<input type="email" name="email" placeholder="Email" />
<div
class="cf-turnstile"
data-sitekey="<YOUR-SITE-KEY>"
data-size="compact"
data-action="newsletter"
></div>
<button type="submit">Subscribe</button>
</form>
<!-- Normal widget for contact form -->
<form action="/contact" method="POST">
<input type="text" name="name" placeholder="Name" />
<input type="email" name="email" placeholder="Email" />
<textarea name="message" placeholder="Message"></textarea>
<div
class="cf-turnstile"
data-sitekey="<YOUR-SITE-KEY>"
data-action="contact"
data-theme="dark"
></div>
<button type="submit">Send</button>
</form>Автоматическая интеграция с формой
Когда вы встраиваете виджет Turnstile в <form> появится невидимое поле ввода с именем cf-turnstile-response создаётся автоматически. Это поле содержит токен проверки и отправляется вместе с остальными данными формы.
<form action="/submit" method="POST">
<input type="text" name="data" />
<div class="cf-turnstile" data-sitekey="<YOUR-SITE-KEY>"></div>
<!-- Hidden field automatically added: -->
<!-- <input type="hidden" name="cf-turnstile-response" value="TOKEN_VALUE" /> -->
<button type="submit">Submit</button>
</form>Явный рендеринг
Явный рендеринг даёт программный контроль над тем, когда и где появляется виджет и как виджеты создаются с помощью функций JavaScript. Такой способ подходит для динамического контента, одностраничных приложений (SPA) и вывода виджета в зависимости от действий пользователя.
Сценарии использования
Cloudflare рекомендует применять явный рендеринг в следующих случаях:
- У вас динамические сайты и одностраничные приложения (SPA).
- Вам нужно управлять моментом создания виджета.
- Вы хотите отображать виджет по условию, в зависимости от действий посетителя.
- Вам нужно несколько виджетов с разными конфигурациями.
- У вас сложные приложения, в которых требуется управлять жизненным циклом виджета.
Внедрение
1. Добавьте скрипт на сайт с явным рендерингом
<script
src="https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit"
defer
></script>2. Создайте элементы-контейнеры
Создайте контейнеры без cf-turnstile в качестве класса.
<div id="turnstile-container"></div>3. Отрисуйте виджеты программно
Вызовите turnstile.render() для создания виджета, когда всё будет готово.
const widgetId = turnstile.render("#turnstile-container", {
sitekey: "<YOUR-SITE-KEY>",
callback: function (token) {
console.log("Success:", token);
},
});Необязательные вызовы
После явного рендеринга виджета Turnstile с ним может потребоваться взаимодействие, в зависимости от задач вашего приложения. Управление состоянием виджета описано в разделах ниже.
Сброс виджета
Если виджет истёк по времени или устарел, сбросить его можно функцией:
turnstile.reset(widgetId);Получение токена ответа
Текущий токен ответа можно получить в любой момент:
const responseToken = turnstile.getResponse(widgetId);Удаление виджета
Когда виджет больше не нужен, его можно удалить со страницы так:
turnstile.remove(widgetId);При этом ни один callback не вызывается, а все связанные элементы DOM удаляются.
Полные примеры явного рендеринга по сценариям
Базовая явная реализация
<!DOCTYPE html>
<html>
<head>
<title>Explicit Rendering</title>
<script
src="https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit"
defer
></script>
</head>
<body>
<form id="login-form">
<input
type="text"
name="username"
placeholder="Username"
autocomplete="username"
/>
<input
type="password"
name="password"
placeholder="Password"
autocomplete="current-password"
/>
<div id="turnstile-widget"></div>
<button type="submit">Login</button>
</form>
<script>
window.onload = function () {
turnstile.render("#turnstile-widget", {
sitekey: "<YOUR-SITE-KEY>",
callback: function (token) {
console.log("Turnstile token:", token);
// Handle successful verification
},
"error-callback": function (errorCode) {
console.error("Turnstile error:", errorCode);
},
});
};
</script>
</body>
</html>Использование обратного вызова onload
<script
src="https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit&onload=onTurnstileLoad"
defer
></script>
<div id="widget-container"></div>
<script>
function onTurnstileLoad() {
turnstile.render("#widget-container", {
sitekey: "<YOUR-SITE-KEY>",
theme: "light",
callback: function (token) {
console.log("Challenge completed:", token);
},
});
}
</script>Расширенная реализация в SPA
<div id="dynamic-form-container"></div>
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit"></script>
<script>
class TurnstileManager {
constructor() {
this.widgets = new Map();
}
createWidget(containerId, config) {
// Wait for Turnstile to be ready
turnstile.ready(() => {
const widgetId = turnstile.render(containerId, {
sitekey: config.sitekey,
theme: config.theme || "auto",
size: config.size || "normal",
callback: (token) => {
console.log(`Widget ${widgetId} completed:`, token);
if (config.onSuccess) config.onSuccess(token, widgetId);
},
"error-callback": (error) => {
console.error(`Widget ${widgetId} error:`, error);
if (config.onError) config.onError(error, widgetId);
},
});
this.widgets.set(containerId, widgetId);
return widgetId;
});
}
removeWidget(containerId) {
const widgetId = this.widgets.get(containerId);
if (widgetId) {
turnstile.remove(widgetId);
this.widgets.delete(containerId);
}
}
resetWidget(containerId) {
const widgetId = this.widgets.get(containerId);
if (widgetId) {
turnstile.reset(widgetId);
}
}
}
// Usage
const manager = new TurnstileManager();
// Create a widget when user clicks a button
document.getElementById("show-form-btn").addEventListener("click", () => {
document.getElementById("dynamic-form-container").innerHTML = `
<form>
<input type="email" placeholder="Email" />
<div id="turnstile-widget"></div>
<button type="submit">Submit</button>
</form>
`;
manager.createWidget("#turnstile-widget", {
sitekey: "<YOUR-SITE-KEY>",
theme: "dark",
onSuccess: (token) => {
// Handle successful verification
console.log("Form ready for submission");
},
});
});
</script>Управление жизненным циклом виджета
Явный рендеринг даёт полный контроль над жизненным циклом виджета.
// Render a widget
const widgetId = turnstile.render("#container", {
sitekey: "<YOUR-SITE-KEY>",
callback: handleSuccess,
});
// Get the current token
const token = turnstile.getResponse(widgetId);
// Check if widget is expired
const isExpired = turnstile.isExpired(widgetId);
// Reset the widget (clears current state)
turnstile.reset(widgetId);
// Remove the widget completely
turnstile.remove(widgetId);Режим выполнения
Режимы выполнения определяют, когда запускаются проверки.
// Render widget but don't run challenge yet
const widgetId = turnstile.render("#container", {
sitekey: "<YOUR-SITE-KEY>",
execution: "execute", // Don't auto-execute
});
// Later, run the challenge when needed
turnstile.execute("#container");Оптимизация производительности и удобства использования
Cloudflare рекомендует запускать скрипт Turnstile как можно раньше при открытии страницы, чтобы к моменту, когда посетитель захочет выполнить на ней какое-либо действие, проверка уже завершилась и виджет был готов к взаимодействию.
Параметры настройки
Неявный и явный способы рендеринга поддерживают одинаковый набор параметров. Наиболее востребованные из них собраны в таблице ниже.
| Опция | Описание | Значения |
|---|---|---|
sitekey |
Sitekey вашего виджета | Обязательная строка |
theme |
Визуальная тема | auto, light, dark |
size |
Размер виджета | normal, flexible, compact |
callback |
Обработчик успешного выполнения | Функция |
error-callback |
Обратный вызов при ошибке | Функция |
execution |
Когда запускать проверку | render, execute |
appearance |
Когда виджет виден | always, execute, interaction-only |
Полный список параметров конфигурации приведён в разделе Конфигурации виджетов.
Тестирование
С тестовым sitekey вы можете проверить работу виджета Turnstile на своей веб-странице, не запуская настоящий Cloudflare Challenge.
См. Тестирование, где это описано подробнее.
Ограничения
Turnstile рассчитан на работу только на страницах, которые используют http:// или https:// схемы URI. Другие протоколы, например file://, для встраивания виджета не поддерживаются.
Требования к безопасности
-
Проверка на стороне сервера обязательна. Токены Turnstile необходимо проверять через Siteverify API: токен может оказаться недействительным, просроченным или уже использованным. Если этого не делать, в вашей реализации останутся серьёзные уязвимости. Без вызова Siteverify настройка Turnstile не завершена, и метрики проверки токенов будут нулевыми в разделе Turnstile Analytics.
-
Токен истекает через 300 секунд (5 минут). Каждый токен проверяется только один раз. Вместо просроченного или уже использованного токена нужно запросить новую проверку.