Установка и настройка
Всё, что нужно, чтобы поставить BotOff на сайт: от установки в один клик в Битриксе до ручной проверки в своём коде.
Быстрый старт
BotOff проверяет каждую отправку формы: браузер посетителя незаметно решает небольшую задачу и получает одноразовый токен, а ваш сервер проверяет этот токен у себя, без запросов в интернет. Если токена нет или он поддельный — форма не доходит до сайта.
Выберите свой вариант:
/botoff/ и одна строка в настройках сервера.Требования
- PHP 7.4 или новее. Расширение
sodiumжелательно (оно есть почти везде); без него задача станет проще для ботов, но всё будет работать. - Возможность записывать файлы в каталог над корнем сайта (туда кладётся конфиг с ключом и журнал).
- Для модуля Битрикса — MySQL или MariaDB и сайт в UTF-8.
- Ни Composer, ни SSH, ни cron не нужны. Подходит обычный виртуальный хостинг.
Скачать
SHA-256: 34edfcee4274e808b99be98aaac596839dd0c6f0737778bcc6a1959f1f0952b3
SHA-256: 5ef1b64360180998589c59ba8fae132c625089cee944da674efe842e62d788f5
Проверить, что архив скачался целиком:
# Linux, macOS
sha256sum botoff-php-*.zip
# Windows (PowerShell)
Get-FileHash botoff-php-*.zip -Algorithm SHA2561С-Битрикс
- Распакуйте архив в
/local/modules/Должна получиться папка/local/modules/botoff.captcha/(в нейinclude.php,install/,lib/). Залить можно по FTP или через файловый менеджер хостинга. - Установите модуль Админка → Marketplace → Установленные решения → в строке «BotOff — защита форм от ботов» нажмите Установить.
- Всё Модуль сразу начинает проверять формы в режиме наблюдения: ничего не блокирует, только записывает. Виджет на страницы вставляется сам.
Что модуль создаёт при установке
| Что | Где | Зачем |
|---|---|---|
| Папка виджета | /botoff/ | Скрипт виджета и точка входа, к которой он обращается |
| Конфиг | /bitrix/php_interface/botoff-config.php | Копия настроек для /botoff/; пересобирается при сохранении настроек |
| Служебные данные | /bitrix/botoff-data/ | Использованные токены, счётчики частоты (закрыто от веба) |
| Таблица | b_botoff_decision | Журнал проверок для отчёта, хранится 30 дней |
| Обработчики | main:OnPageStart, main:OnEndBufferContent | Проверка формы до компонентов и вставка виджета перед </body> |
| Агент | \BotOff\Bitrix\Agent::cleanup(); | Раз в сутки удаляет записи журнала старше 30 дней |
Где что найти в админке
- Настройки → BotOff → Отчёт — сколько отправок прошло и сколько было бы отклонено, по каким причинам и на каких адресах.
- Настройки → Настройки модулей → BotOff — режим, адреса форм, исключения.
Настройки модуля
| Поле | Что делает |
|---|---|
| Режим | Наблюдение — проверять и записывать; Защита — блокировать; Выключено — модуль ничего не делает. |
| Какая форма на каком адресе | По строке на адрес: /путь/ => действие. От действия зависит строгость (см. профили). Всё, что не указано, считается обычной формой form. |
| Не проверять адреса | По строке на адрес, совпадение по началу пути. Для AJAX-обработчиков, которые отправляются не из <form>. |
| Отклонять формы без токена | Если включено, форма от посетителя с выключенным JavaScript в режиме защиты не пройдёт. Боты почти всегда без JavaScript. |
| Подключать виджет автоматически | Если выключить — вставьте тег сами (ниже). |
| Проверок в минуту с одной подсети | Ограничение частоты задач. По умолчанию 60; меньше не ставьте — за одним адресом мобильного оператора сидят тысячи людей. |
| IP посетителя брать из | Если сайт за прокси или Cloudflare, выберите нужный заголовок, иначе все посетители будут «с одного IP». |
Пример поля «Какая форма на каком адресе» (такие строки стоят по умолчанию):
/auth/ => login
/login/ => login
/personal/register/ => signup
/personal/order/make/ => orderПример поля «Не проверять адреса»:
/ajax/calc.php
/local/ajax/
/api//bitrix/tools/sale_ps_result.php), обмен с 1С, админка, /bitrix/services/, /bitrix/tools/, AJAX корзины и оформления заказа (sale.order.ajax, sale.basket.basket, catalog.*).Если виджет вставляете сами
Когда автоматическая вставка выключена, добавьте в шаблон сайта перед </body>:
<?php
if (\Bitrix\Main\Loader::includeModule('botoff.captcha')) {
// false — без привязки в HTML: безопасно для композитного кеша
echo \BotOff\Bitrix\Settings::boot()->scriptTag('/botoff', null, false);
}
?>Композитный сайт и HTML-кеш
Поддерживается. Модуль вставляет виджет без персональных данных, поэтому кешированная страница одинакова для всех, а привязку токена к сессии сервер берёт из cookie в момент проверки.
Обновление
Залейте новую версию поверх папки /local/modules/botoff.captcha/. Настройки и журнал сохраняются. Файлы виджета в /botoff/ обновятся при переустановке модуля, либо скопируйте их из install/public/botoff/ вручную.
<form> (например, через BX.ajax по клику), в режиме защиты придётся либо исключить, либо отправлять с токеном (как) — отчёт покажет такие адреса.Любой сайт на PHP
https://ваш-сайт/botoff/?r=setup: там одна строка для автозащиты, уже с правильным путём под ваш сервер, и кнопка «Копировать». Режим, адреса и исключения дальше меняются в кабинете. Ниже — ручная установка из общего архива.- Залейте папку
botoff/в корень сайта Из архива PHP-пакета. Должно получитьсяhttps://ваш-сайт/botoff/index.php. - Включите автозащиту форм
Нужно, чтобы файл
botoff/guard.phpвыполнялся перед каждым запросом. Выберите способ, который подходит вашему хостингу (ниже). - Вставьте тег виджета в общий шаблон страниц, перед
</body>. - Проверьте, что всё на месте, и неделю понаблюдайте.
Шаг 2. Автозащита: три способа
Способ А — .htaccess (Apache с PHP как модулем). Добавьте строку в .htaccess в корне сайта, путь — полный, от корня сервера:
php_value auto_prepend_file "/home/user/site.ru/public_html/botoff/guard.php"php_value там запрещён. Уберите строку и используйте способ Б.Способ Б — .user.ini (PHP-FPM, nginx, большинство виртуальных хостингов). Создайте файл .user.ini в корне сайта:
auto_prepend_file = /home/user/site.ru/public_html/botoff/guard.php.user.ini раз в 5 минут — изменения вступят в силу не сразу.Способ В — строка в коде. Если у сайта одна точка входа (index.php), подключите защиту первой строкой:
<?php
require __DIR__ . '/botoff/guard.php';
// ... дальше ваш код как былauto_prepend_file уже указан другой файл, второй указать нельзя — добавьте require '/путь/к/botoff/guard.php'; в начало того файла.Шаг 3. Тег виджета
Простой вариант — статический тег:
<script src="/botoff/botoff.js" data-endpoint="/botoff" defer></script>Лучше — через PHP: тогда токен привяжется к сессии посетителя, а виджет узнает, какая форма на каком адресе (routes из конфига):
<?php
require_once $_SERVER['DOCUMENT_ROOT'] . '/botoff/bootstrap.php';
echo \BotOff\BotOff::boot()->scriptTag('/botoff');
?>Шаг 4. Проверка
Откройте в браузере https://ваш-сайт/botoff/index.php?r=health. Должно быть:
{"ok":true,"v":"0.1.7","alg":"argon2id-v1","network":"up"}alg = argon2id-v1 — sodium есть, всё отлично. sha256-v1 — sodium выключен: попросите хостинг включить расширение, защита станет сильнее.
При первом запросе BotOff сам создаст на уровень выше корня сайта:
/home/user/site.ru/
├── botoff-config.php ← ключ и настройки (никому не передавайте)
├── botoff-data/ ← журнал и служебные файлы
└── public_html/
└── botoff/ ← то, что вы залилиbotoff-config.php две строки — 'site_key' => 'bo_s_…' и 'site_secret' => 'bo_k_…'. Дальше режим, JSON-адреса и исключения меняются на странице «Настройки защиты» сайта в кабинете: пакет сам заберёт их (подписанными ключом сайта) в течение 10 минут, а отчёт и проверка сайта появятся там же. Если botoff.app недоступен, сайт работает на последних полученных настройках.Если у вас nginx без Apache
Файл .htaccess в папке botoff/ закрывает служебные файлы только на Apache. Для nginx добавьте в конфиг сайта:
location ~ ^/botoff/(src/|bootstrap\.php|guard\.php|report\.php|config\.example\.php) {
return 404;
}Для Caddy:
@botoff_internal path /botoff/src/* /botoff/bootstrap.php /botoff/guard.php /botoff/report.php /botoff/config.example.php
respond @botoff_internal 404WordPress
Отдельный плагин ещё в работе, а пока ставится PHP-пакет — так же надёжно.
- Залейте
botoff/в корень WordPress (рядом сwp-config.php). - Автозащита — способ Б (
.user.ini) из раздела выше, или первой строкой вwp-config.php:wp-config.php, в самом начале<?php require __DIR__ . '/botoff/guard.php'; - Виджет — в
functions.phpвашей темы (лучше дочерней):functions.php$botoff_widget = function () { require_once ABSPATH . 'botoff/bootstrap.php'; echo \BotOff\BotOff::boot()->scriptTag('/botoff'); }; add_action('wp_footer', $botoff_widget); // все страницы сайта add_action('login_footer', $botoff_widget); // wp-login.php: у неё свой шаблон без wp_footer
Комментарии, вход (wp-login.php), регистрация и обычные формы защищаются сразу. Чтобы вход проверялся строже, добавьте в конфиг:
'routes' => [
'#^/wp-login\.php#' => 'login',
'#^/wp-comments-post\.php#' => 'comment',
],/wp-json/), по умолчанию не проверяются: /wp-json/ в исключениях, иначе сломались бы REST-запросы самого WordPress. Для таких форм проверяйте токен в обработчике плагина (как).admin-ajax.php с сайта (например, «В корзину» в WooCommerce) автозащита проверяет. Если в отчёте на этом адресе все отправки без токена — передавайте токен в запросе или добавьте адрес в исключения.Кабинет: отчёт, проверка сайта, настройки, оповещения
Кабинет на botoff.app/account работает для любого сайта — с Битриксом и без. Вход по коду из письма, пароля нет.
- Добавьте сайт и подтвердите его. Файлом в корне, файлом в папке
botoff/(он уже лежит в архиве из кабинета) или записью в DNS. - Впишите ключи сайта. Битрикс: Настройки → Настройки модулей → BotOff → «Сеть BotOff». PHP-пакет: ключи уже в архиве из кабинета, либо две строки
site_keyиsite_secretв конфиге. - Готово. Сайт раз в 10 минут присылает сводку: адреса форм, итоги проверок, тип клиента и имена полей — без IP-адресов и того, что вводят посетители.
Что есть в кабинете
- Отчёт — тот же, что в админке Битрикса: график, кто ломится, почему отклонено, живые подсказки «этот адрес стоит защищать», «а это похоже на вебхук — не проверять». Кнопки применяют настройку на сайте сами.
- Проверка сайта — смотрит сайт снаружи, как посетитель: движок, формы, куда скрипты отправляют данные. Только чтение, формы не отправляются.
- Настройки защиты — для сайтов без админки: режим, JSON-адреса, исключения, адрес → действие. Сайт забирает их со сводкой, проверяя подпись ключом сайта. Если botoff.app недоступен — работает на последних полученных.
- Оповещения — письмо об атаке (порог выбираете сами), если сайт перестал выходить на связь, недельная сводка. Отписка в один клик из любого письма.
- Архив для сайта — PHP-пакет с вписанными ключами и файлом подтверждения. Последний шаг — открыть
https://ваш-сайт/botoff/?r=setup: там строка для автозащиты с точным путём под ваш сервер и кнопка «Копировать».
Какие формы защищаются
Автозащита проверяет запросы POST с телом формы (application/x-www-form-urlencoded или multipart/form-data). Не проверяются:
- GET-запросы (поиск, фильтры);
- JSON-запросы — у API обычно своя авторизация;
- адреса из списка исключений.
Виджет сам находит на странице все формы с method="post", в том числе появившиеся позже (модальные окна), и добавляет в них два скрытых поля: botoff-token и поле-ловушку botoff-hp. До вашего кода эти поля не доходят — защита их убирает.
Действия и строгость
У каждой формы есть «действие». Токен выдаётся под конкретное действие: токен от формы обратной связи не подойдёт для входа. Действие задаёт строгость:
| Профиль | Действия | Макс. риск | Если наша сеть недоступна |
|---|---|---|---|
| forms | form, feedback, comment | 70 | пускаем |
| shop | order, review | 60 | пускаем |
| auth | login, signup, reset | 50 | пускаем, только если сеть действительно лежит |
| strict | payment | 40 | не пускаем |
Пока сайт не подключён к сети узлов BotOff (как сейчас у всех — сеть в разработке), все проверки локальные и все профили работают.
Как указать действие
По адресу — в конфиге (routes) или в настройках модуля Битрикса. Виджет получит ту же таблицу и сам выдаст токен с нужным действием:
'routes' => [
'#^/auth/#' => 'login',
'#^/register/#' => 'signup',
'#^/cart/checkout#' => 'order',
],Атрибутом формы — если адресов несколько или удобнее в HTML. Атрибут главнее таблицы, но сервер ждёт действие по таблице — они должны совпадать:
<form method="post" action="/auth/" data-botoff-action="login">Отключить защиту для конкретной формы (виджет её не тронет; на сервере добавьте адрес в исключения):
<form method="post" action="/subscribe" data-botoff="off">Режим наблюдения и включение защиты
После установки BotOff ничего не блокирует: проверяет каждую форму и записывает, что было бы отклонено. Так вы увидите, не задевает ли защита что-то своё — например, AJAX-запрос, который отправляется без формы.
Где смотреть
- Битрикс — Настройки → BotOff → Отчёт. У каждого адреса — последний отказ: какие поля пришли, каким клиентом и откуда. Кнопка «В исключения» рядом.
- PHP-пакет — консольный отчёт по журналу (нужен SSH):
консольБез SSH скачайте по FTP файл
php botoff/report.php 7botoff-data/decisions.log— по строке JSON на каждую проверку.
Как читать
- Отказы
honeypot,replayed, отправки без браузера (curl, python) — это боты, всё правильно. - На адресе все отправки отклонены с
missing_token, и это ваша форма — значит, она отправляется не через<form>: исключите адрес или передавайте токен. wrong_action— действие формы не совпало с таблицейroutes. Поправьте таблицу или атрибут.
Включить защиту
Битрикс — кнопка «Включить защиту» в отчёте. PHP-пакет — в конфиге:
'mode' => 'enforce',Посетитель, не прошедший проверку, увидит страницу «Не получилось отправить форму» с кнопкой вернуться. AJAX-запрос с заголовком Accept: application/json получит ответ 403:
{"error":"botoff_rejected","message":"Не удалось подтвердить, что запрос отправил человек. Обновите страницу и попробуйте ещё раз."}Как BotOff ловит ботов
Одной проверки мало: бот подделает то, что проверяют. Поэтому BotOff сверяет несколько слоёв и поднимает цену для тех, кто не сходится. Человек ничего этого не замечает.
| Слой | Что проверяется | Что получает бот |
|---|---|---|
| Задача в браузере | Argon2id: память и процессор. Сложность растёт с риском — человеку доли секунды, подозрительному клиенту в десятки раз дольше. Проверка на сервере при этом не дорожает. | Платит за каждую попытку |
| Соединение | Отпечаток TLS (JA4) и HTTP/2 в ALPN — только в сети BotOff. | Скрипт, назвавшийся браузером, видно сразу |
| Заголовки | Client Hints (версия, платформа, мобильный) против User-Agent, служебные Sec-Fetch-*. | «Chrome» без заголовков Chrome |
| Окружение | Признаки автоматизации, безголового браузера, отрисовки без видеокарты, «телефона» без сенсорного экрана. Наружу — только флажки «да/нет». | Puppeteer, Playwright, Selenium |
| Поведение | Модель в браузере: дрожание руки, ритм печати, удержание клавиш, клики не точно в центр, события от скрипта. Наружу — только оценка 0–100. | Ровные линии и печать «метрономом» |
| Ловушки | Скрытая ссылка, форма-приманка, обращения к .env, .git и адресам чужих движков. | Подсеть на сутки получает задачу дороже — на этом сайте и во всей сети |
| Токен | Подпись, одноразовость, 2 минуты, привязка к сессии, форме и сайту. | Перепродать или повторить нельзя |
Фильтр содержимого: живой спам, который капча не видит
Капча отличает человека от программы, но не может остановить человека, которому платят за рассылку. Для этого — фильтр содержимого. Он работает только на сервере сайта: значения полей никуда не уходят и не записываются, в отчёт попадает лишь название правила.
| Правило | Что делает | По умолчанию |
|---|---|---|
| Одноразовые почты | Отклоняет temp-mail, mailinator, 10minutemail и ещё ~200 сервисов, включая их поддомены. Можно добавить свои домены. | включено |
| Ссылки | Больше N ссылок в тексте — отказ. Ссылка в поле «Имя» — отказ всегда: так пишут только рекламу. | не больше 3 |
| Стоп-слова | Ваш список слов и выражений, можно регулярные выражения: /вы\s+выиграли/. | пусто |
| Скорость набора | Быстрее N символов в секунду печатает только скрипт. Автозаполнение и вставку из буфера не оцениваем. | 25 знаков/с |
| Белый и чёрный списки | IP и подсети, почты и домены. Белый список IP — без всякой проверки (офис, свой сервер), чёрный — отказ даже с верным токеном. | пусто |
Где настроить: Битрикс — Настройки → Настройки модулей → BotOff → «Фильтр содержимого». PHP-пакет — кабинет → сайт → «Настройки защиты», или ключи max_links, stop_words, deny_ips и другие в конфиге (справочник ниже).
Страны и файрвол на уровне сайта
Файрвол срабатывает на любом запросе к сайту, раньше самого сайта. Страна определяется на вашем сервере по свободной базе DB-IP: файл скачивается с botoff.app, только если вы указали страны, и обновляется раз в месяц.
| Режим | Что происходит |
|---|---|
| Выключен | Правил по странам нет. |
| Только формы (по умолчанию) | Из запрещённых стран нельзя отправить форму. Сайт открывается всем — это безопасно для SEO и рекламы. |
| Весь сайт | Запрещённым странам и чёрному списку IP — 403 на любой странице. Странам из списка «Проверять браузер» и подсетям, попавшим в ловушки, — страница «Проверяем, что вы человек» на секунду-две, после неё сутки без вопросов. |
/botoff/ и белый список IP. В режиме наблюдения он только записывает в отчёт. Здесь обычно и ломаются облачные «спам-файрволы»: блокируют поисковики, мобильных операторов и вебхуки оплат — у BotOff это исключено с первой минуты.Где настроить: Битрикс — вкладка «Файрвол» в настройках модуля. PHP-пакет — «Настройки защиты» в кабинете, или ключи firewall, deny_countries, challenge_countries в конфиге.
Страна по IP: «IP Geolocation by DB-IP» (db-ip.com), лицензия CC BY 4.0.
AJAX и одностраничные сайты
Обычно ничего делать не нужно. Виджет сам добавляет токен в заголовок X-BotOff-Token у запросов fetch и XMLHttpRequest (а значит, и jQuery, axios, BX.ajax), если это POST на ваш же сайт с телом формы (FormData, URLSearchParams, строка a=1&b=2) и адрес не в исключениях. GET-запросы, чужие домены и счётчики аналитики виджет не трогает.
JSON-запросы проверяются только на адресах, которые вы перечислили, — в Битриксе поле «Проверять и JSON-запросы на адресах», в конфиге:
'protect_json' => ['#^/ajax/api\.php#', '#^/api/order#'],Виджет получит этот список и сам добавит токен в такие запросы.
Если нужен ручной контроль
Передать токен самому — например, когда запрос уходит на другой ваш домен:
const token = await BotOff.getToken('feedback');
await fetch('/ajax/feedback.php', {
method: 'POST',
headers: { 'X-BotOff-Token': token },
body: new FormData(form),
});BotOff.getToken('order').then(function (token) {
$.ajax({
url: '/ajax/order.php',
method: 'POST',
headers: { 'X-BotOff-Token': token },
data: $(form).serialize(),
});
});const token = await BotOff.getToken('login');
await axios.post('/api/login', formData, { headers: { 'X-BotOff-Token': token } });getToken() должно совпадать с тем, что сервер ждёт на этом адресе (routes). Если адрес не указан в таблице — используйте 'form'.data-intercept="off" у тега виджета — тогда токен в AJAX-запросы добавляете сами, как выше.Чтобы токен был готов заранее (без паузы при отправке), прогрейте его, когда пользователь начал заполнять форму:
input.addEventListener('focus', () => BotOff.prefetch('login'), { once: true });Для React, Vue и других SPA, где формы создаются скриптом и отправляются через fetch, можно выключить автоматический поиск форм и брать токен вручную:
<script src="/botoff/botoff.js" data-endpoint="/botoff" data-auto="off" defer></script>Проверка в своём коде
Если автозащита вам не подходит или нужно проверить обработчик, который она не видит (JSON, REST), проверяйте токен сами:
<?php
require_once $_SERVER['DOCUMENT_ROOT'] . '/botoff/bootstrap.php';
$check = \BotOff\BotOff::boot()->verify('login'); // токен берётся из поля botoff-token или заголовка X-BotOff-Token
if (!$check['ok']) {
http_response_code(403);
exit('Проверка не пройдена: ' . $check['error']);
}
// ... дальше обычная обработка формыТокен можно передать явно — например, из JSON-тела:
$data = json_decode(file_get_contents('php://input'), true);
$check = \BotOff\BotOff::boot()->verify('feedback', $data['botoffToken'] ?? '');Что возвращает verify():
// прошёл
['ok' => true, 'risk' => 10, 'mode' => 'lite', 'reasons' => ['lite'], 'action' => 'login', 'iat' => 1792332000]
// не прошёл
['ok' => false, 'error' => 'replayed']
['ok' => false, 'error' => 'risk_too_high', 'risk' => 85, 'reasons' => ['lite', 'automation'], ...]Битрикс: в обработчике, который в исключениях
Модуль и так проверяет все формы до компонентов. Ручная проверка нужна для адресов из списка «Не проверять» — например, своего AJAX-обработчика с JSON:
use Bitrix\Main\Loader;
if (Loader::includeModule('botoff.captcha')) {
$check = \BotOff\Bitrix\Settings::boot()->verify('order');
if (!$check['ok']) {
$this->arResult['ERRORS'][] = 'Не удалось подтвердить, что вы не робот. Обновите страницу.';
return;
}
}Laravel: middleware
// app/Http/Middleware/BotOffCheck.php
public function handle($request, Closure $next, string $action = 'form')
{
require_once public_path('botoff/bootstrap.php');
$check = \BotOff\BotOff::boot(base_path('botoff-config.php'))->verify($action, $request->input('botoff-token') ?? $request->header('X-BotOff-Token'));
abort_unless($check['ok'], 403, 'Проверка не пройдена');
return $next($request);
}
// routes/web.php
Route::post('/login', [AuthController::class, 'login'])->middleware('botoff:login');replayed. Если стоит автозащита — она уже проверила токен до вашего кода, и повторно его проверять не нужно.Переезд с reCAPTCHA и Яндекс SmartCaptcha
Проще всего — убрать старую капчу и поставить автозащиту: формы будут проверяться без правок обработчиков. Если хотите оставить свой код проверки, BotOff отвечает в том же формате, что и они.
- Уберите старый виджет со страниц (скрипт
recaptcha/api.jsилиsmartcaptcha.yandexcloud.netи блок капчи в форме) и поставьте тег BotOff. - Узнайте локальный секрет — один раз, из консоли или временного скрипта:
консоль, из корня сайта
php -r 'require "botoff/bootstrap.php"; echo \BotOff\BotOff::boot()->localSecret(), "\n";' - Поменяйте адрес проверки, секрет и имя поля в обработчике:
Было Стало Адрес https://www.google.com/recaptcha/api/siteverifyhttps://smartcaptcha.yandexcloud.net/validatehttps://ваш-сайт/botoff/index.php?r=siteverifyСекрет ключ из консоли Google / Яндекса результат шага 2 Поле с токеном g-recaptcha-response/smart-tokenbotoff-token
$resp = file_get_contents('https://site.ru/botoff/index.php?r=siteverify', false, stream_context_create(['http' => [
'method' => 'POST',
'header' => 'Content-Type: application/x-www-form-urlencoded',
'content' => http_build_query([
'secret' => BOTOFF_SECRET,
'response' => $_POST['botoff-token'] ?? '',
'remoteip' => $_SERVER['REMOTE_ADDR'],
]),
]]));
$r = json_decode($resp, true);
if (!$r['success']) { /* отказ */ }Ответ подходит и под проверку reCAPTCHA (success, score), и под SmartCaptcha (status):
{"success":true,"status":"ok","challenge_ts":"2026-10-18T12:00:10Z","hostname":"site.ru","action":"form","score":0.9,"error-codes":[]}verify() (как) — это и быстрее.Все настройки
Если сайт подключён к кабинету и там сохранены настройки, то mode, routes, exclude, protect_json, require_token, lite_rate и ip_header берутся из кабинета, а файл нужен только для ключей. Конфиг — обычный PHP-файл, который возвращает массив: botoff-config.php на уровень выше корня сайта (в Битриксе им управляет модуль). Создаётся сам при первом запуске; указывать нужно только то, что меняете. Другой путь к конфигу задаётся переменной окружения BOTOFF_CONFIG или аргументом BotOff::boot('/путь/к/конфигу.php').
| Ключ | По умолчанию | Что это |
|---|---|---|
lite_key | создаётся сам | Секретный ключ сайта, 64 hex-символа. Никому не передавайте; при смене все выданные токены станут недействительны. |
mode | 'observe' | 'observe' — наблюдение, 'enforce' — защита. |
observe_until | 0 | Unix-время, до которого наблюдать; потом защита включится сама. 0 — наблюдать, пока не переключите вручную. |
routes | [] | Регулярное выражение пути → действие. Пример в разделе формы. |
default_action | 'form' | Действие для адресов, которых нет в routes. |
actions | см. профили | Действие → профиль (forms, shop, auth, strict). Свои действия добавляйте сюда. Как и exclude, заменяет список целиком — перечислите и стандартные. |
exclude | платёжки, 1С, админки | Регулярные выражения путей, которые автозащита не трогает. Заменяет список целиком — см. пример ниже. |
protect_json | [] | Регулярные выражения путей, где проверять и JSON-запросы (токен в заголовке X-BotOff-Token). |
require_token | true | Отклонять формы без токена (у посетителя выключен JavaScript). |
origins | текущий домен | С каких адресов разрешены формы, напр. ['https://site.ru', 'https://www.site.ru']. Нужен, если сайт открывается на нескольких доменах. |
lite_rate | 60 | Задач в минуту на подсеть /24 (IPv4) или /56 (IPv6). |
content, block_disposable | true | Фильтр содержимого и отказ одноразовым почтам (подробно). |
max_links, min_typing_cps | 3, 25 | Лимит ссылок в тексте и скорость набора, знаков в секунду. 0 — не проверять. |
stop_words, disposable_extra | [] | Стоп-слова (подстрока или /регулярка/) и свои одноразовые домены. |
allow_ips, deny_ips | [] | IP и подсети: без проверки / всегда отказ. ['203.0.113.10', '198.51.100.0/24'] |
allow_emails, deny_emails | [] | Почты: адрес, @домен или домен. |
firewall | 'forms' | 'off', 'forms' или 'site' (подробно). |
deny_countries, challenge_countries | [] | Коды стран ISO: ['CN', 'VN']. Проверка браузера — только в режиме site. |
fw_trapped | true | В режиме site — проверка браузера для подсетей из ловушек. |
ip_header | '' | Откуда брать IP посетителя за прокси: 'HTTP_X_REAL_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_CF_CONNECTING_IP' (Cloudflare). |
session_cookie | 'PHPSESSID' | Cookie сессии, к которой привязывается токен. |
data_dir | рядом с конфигом | Каталог журнала и служебных файлов. Должен быть доступен PHP на запись и закрыт от веба. |
site_key, site_secret | пусто | Ключи сайта из кабинета: сеть BotOff, отчёт и настройки в кабинете. Адреса узлов и корневые ключи сети пакет знает сам; nodes, root_keys, revoked задавайте, только если вам их выдали отдельно. |
Пример конфига интернет-магазина за Cloudflare:
<?php
return [
'lite_key' => '…останется тот, что создался…',
'mode' => 'enforce',
'origins' => ['https://shop.ru', 'https://www.shop.ru'],
'ip_header' => 'HTTP_CF_CONNECTING_IP',
'routes' => [
'#^/login#' => 'login',
'#^/register#' => 'signup',
'#^/checkout#' => 'order',
'#^/reviews/add#' => 'review',
],
// свои исключения + стандартные (иначе стандартные пропадут)
'exclude' => array_merge(\BotOff\Config::defaultExclude(), [
'#^/api/#',
'#^/ajax/calc\.php#',
]),
];Справочник виджета
Атрибуты тега <script>
| Атрибут | По умолчанию | Что это |
|---|---|---|
data-endpoint | /botoff | Где лежит папка BotOff на сайте. |
data-action | form | Действие по умолчанию для форм и getToken() без аргумента. |
data-auto | включено | off — не искать формы автоматически, только getToken(). |
data-intercept | включено | off — не добавлять токен в fetch и XMLHttpRequest автоматически. |
data-skip, data-json | из конфига | Исключения и JSON-адреса. Ставит scriptTag() сам. |
data-routes | из конфига | Таблица адрес → действие. Ставит scriptTag() сам. |
data-bind | из сессии | Привязка к сессии. Ставит scriptTag() сам. |
data-worker-url | рядом со скриптом | Свой адрес botoff-worker.js, если файлы лежат на CDN. |
Атрибуты форм
data-botoff-action="login" | Действие этой формы. |
data-botoff="off" | Не трогать эту форму. |
JavaScript
BotOff.getToken(action?) | Промис с одноразовым токеном. Если токен заготовлен — отдаёт сразу. |
BotOff.prefetch(action?) | Заготовить токен заранее. |
BotOff.protect(form) | Подключить защиту к форме вручную (при data-auto="off"). |
BotOff.version | Версия виджета. |
Пока токен получается, у формы есть класс botoff-pending и aria-busy="true" — можно показать индикатор:
form.botoff-pending button[type=submit] { opacity: .6; pointer-events: none; }Коды ошибок
| Код | Что значит | Что делать |
|---|---|---|
missing_token | Форма пришла без токена | Бот — всё правильно. Если это ваша форма: виджет не подключён на странице или запрос идёт не через <form> → исключение или токен в заголовке. |
honeypot | Заполнено поле-ловушка | Бот. |
replayed | Токен уже использован | Бот или двойная проверка в вашем коде (см. примечание). |
expired | Токен старше 2 минут | Обычно — страница долго была открыта; виджет сам берёт свежий. Проверьте, что часы сервера идут верно. |
wrong_action | Токен выдан для другой формы | Поправьте routes или data-botoff-action. |
wrong_origin | Токен получен на другом домене | Сайт на нескольких доменах — перечислите их в origins. |
bind_mismatch | Токен из другой сессии | Токен украли или перепродали. Если массово у людей — проверьте session_cookie. |
risk_too_high | Признаки автоматизации | Бот на настоящем браузере. Причины — в поле reasons. |
mode_not_allowed | Упрощённая проверка запрещена для этой формы | Появится, когда заработает сеть узлов. |
bad_signature, malformed | Поддельный или повреждённый токен | Бот. Если после смены lite_key — пройдёт само. |
origin_mismatch | Задачу запросили с чужого домена (в ответе /botoff/) | Добавьте домен в origins. |
rate_limited | Слишком много задач с одной подсети | Атака — или поднимите lite_rate, если у вас много посетителей из одной сети. |
Вопросы
Что увидит посетитель?
Ничего. Задача решается в фоне, пока человек заполняет форму, обычно за 0,3–1 секунды. Если он успел нажать «Отправить» раньше — форма уйдёт через мгновение, сама.
А если у посетителя выключен JavaScript?
Без JavaScript токен не получить. По умолчанию такая форма в режиме защиты не пройдёт (почти все боты — без JavaScript). Если для вас важны такие посетители, поставьте 'require_token' => false — тогда они пройдут, но и простые боты тоже.
Это законно с точки зрения 152-ФЗ и GDPR?
BotOff в режиме Lite не передаёт никаких данных за пределы вашего сервера. В журнал пишутся адрес формы, решение и имена полей — без значений, без IP-адресов в открытом виде.
Что написать в политике конфиденциальности своего сайта?
Ничего особенного: BotOff не собирает и не хранит персональные данные ваших посетителей — ни в Lite, ни в сети. Достаточно упомянуть защиту форм. Пример абзаца:
Для защиты форм сайта от автоматизированных отправок (ботов) используется сервис BotOff. Сервис не собирает и не хранит персональные данные посетителей: проверка выполняется в браузере, а сайт получает только одноразовую оценку риска.Удобно ли людям с ограниченными возможностями?
Да: нет картинок и головоломок, ничего не нужно видеть или слышать. Пользователи экранных дикторов и клавиатуры не получают штрафов за «нечеловеческое» поведение.
Нагружает ли это сервер?
Почти нет. Выдать задачу — доли миллисекунды, проверить решение — около 40 мс на одну отправку формы, проверить токен — меньше 2 мс. Тяжёлая часть работы — у браузера посетителя.
Не навредит ли это SEO?
Нет. Поисковые роботы (Яндекс, Google) только читают страницы — GET-запросы, а BotOff проверяет лишь отправку форм (POST). Робот получает ту же страницу, что и человек, с тем же кодом ответа, без задержек и без подмены содержимого. Служебные адреса /botoff/ отдают заголовок X-Robots-Tag: noindex и в индекс не попадут. Виджет весит около 20 КБ в сжатом виде, грузится с defer и не мешает отрисовке; тяжёлая часть (решение задачи) запускается, только когда человек начал заполнять форму.
Боты точно не пройдут?
Ни одна капча не даёт гарантии: против живых людей, которым платят за решение, защиты нет ни у кого. BotOff делает массовую атаку дорогой: каждая попытка стоит памяти и процессора, токен одноразовый, живёт 2 минуты и привязан к сессии, форме и сайту. Простые боты не проходят вовсе.
Не навредят ли ловушки SEO?
Нет. Скрытая ссылка помечена rel="nofollow noindex", а её адрес отвечает 404 с X-Robots-Tag: noindex — в индекс он не попадёт. Настоящие роботы Google, Яндекса, Bing, Apple и Mail.ru проверяются по обратному DNS и в ловушку не засчитываются.
Что, если бот и живой человек сидят за одним адресом мобильного оператора?
Ловушки и подозрения не блокируют, а повышают сложность задачи. Человек из той же подсети пройдёт проверку, просто подождёт на секунду-другую дольше. Пометка живёт сутки.
Как узнать, что на сайт напали?
В кабинете включите оповещения: письмо придёт, когда за час отказов больше выбранного порога, — не чаще раза в 6 часов на сайт. Если сайт в режиме наблюдения, письмо подскажет включить защиту.
Чем BotOff отличается от облачных антиспам-модулей?
Проверка идёт на вашем сервере, а не в чужом облаке, поэтому формы работают, даже если наши серверы недоступны. Ни имена, ни почты, ни тексты сообщений, ни IP-адреса посетителей к нам не уходят. Вебхуки платёжных систем, обмен с 1С и админка с первой минуты в исключениях, а первую неделю BotOff только наблюдает — заказы и оплаты не потеряются.
Сайт за Cloudflare или другим прокси
Укажите ip_header, иначе BotOff будет видеть всех посетителей с адреса прокси и сработает ограничение частоты. Для Cloudflare — 'HTTP_CF_CONNECTING_IP'.
Сайт на нескольких доменах
Перечислите все в origins: ['https://site.ru', 'https://www.site.ru', 'https://site.com']. Токен, полученный на одном домене, на другом не подойдёт — так задумано.
Можно ли на Тильду, Wix, конструкторы?
Пока нет: формы там уходят на серверы конструктора, а не на ваш, и проверить токен негде. Для Тильды готовим вариант через вебхук.
Что будет, если ваши серверы упадут?
Сейчас — ничего: в режиме Lite всё работает на вашем сервере, мы не участвуем. Когда появится сеть узлов, при её недоступности сайт сам переключится обратно на Lite, формы продолжат работать.
Удаление
Битрикс
Marketplace → Установленные решения → BotOff → Удалить. Можно оставить журнал в базе (галочка). Модуль уберёт обработчики, агент, папку /botoff/, конфиг и служебные данные. Затем удалите /local/modules/botoff.captcha/.
PHP-пакет
- Уберите строку
auto_prepend_file(илиrequireв коде) и подождите 5 минут, если это был.user.ini. - Уберите тег виджета из шаблона.
- Удалите папку
botoff/, файлbotoff-config.phpи папкуbotoff-data/.
guard.php из auto_prepend_file и сайт ответит ошибкой.