Защита формы обратной связи от спама без reCAPTCHA
Honeypot, time-trap, локальные капчи и проверки контента. Как блокировать ботов в форме заявок без Google reCAPTCHA в 2026.
Содержание
- Откуда вообще берётся спам в форме
- Honeypot: невидимое поле для ботов
- Time-trap: проверка времени заполнения
- Проверка referer и Origin
- CSRF-токен в сессии
- Контент-фильтры
- Простая локальная капча
- Лимит запросов с одного IP
- Теневое отбрасывание спама
- Логирование подозрительных запросов
- Что я ставлю на каждом клиентском проекте
- Что почитать дальше
- Частые вопросы
Три приёма без капчи закрывают почти весь спам: скрытое поле-ловушка отсекает около 80 процентов, проверка времени заполнения ещё 15, проверка источника запроса оставшееся. Капча от Google при этом добавляет к странице 200-500 мс и передаёт данные посетителей за рубеж.
Forma обратной связи на маленьком сайте получает 5-50 заявок в день, из которых половина спам, если не защищаться никак. С reCAPTCHA доля спама падает до единиц процента, но reCAPTCHA это Google: трекинг, передача персональных данных в США, замедление страницы на 200-500 мс, и периодический риск, что в России её просто заблокируют. У меня в 2024 году пара клиентов уже теряли заявки именно из-за того, что reCAPTCHA не загружалась с части русских IP.
Хорошая новость: reCAPTCHA в 95 процентах случаев избыточна. Маленький сайт услуг можно отлично защитить простыми приёмами на стороне сервера, без единого пиксела от Google. Ниже разберу, что я ставлю на каждом клиентском проекте.

Откуда вообще берётся спам в форме
Чтобы выбирать защиту по делу, надо понимать, кто вас заваливает.
Универсальные боты-парсеры. Сканируют интернет в поисках любых форм, заполняют их по шаблону, отправляют. Обычно ищут поля name, email, message. В сообщении ставят рекламу казино, наркотиков, фейковых SEO-услуг. Это 80 процентов спама. Боты тупые, защита от них работает на простых трюках.
Целевые боты под конкретный движок. Знают, как выглядит форма WordPress Contact Form 7 или WPForms, и отправляют запрос напрямую на эндпоинт, минуя саму форму. Это 15 процентов спама. Защищаемся проверками на стороне сервера.
Полуручной спам с офшорной фермой. Реальный человек заполняет десятки форм в день за маленькие деньги. Видит, что у вас сайт, и присылает «коммерческое предложение» по seo, разработке, рекламе. Это 5 процентов спама. От этого защититься техническими средствами почти нельзя, только фильтрацией по содержанию.
Цель не убить весь спам в ноль. Цель оставить столько, сколько вы готовы пробежать глазами в почте за минуту.
Honeypot: невидимое поле для ботов
Самый простой и самый эффективный приём. Добавляете в форму скрытое поле, которое пользователь не видит и не заполняет, а бот заполняет автоматически.
Как делается. В HTML-форме рядом с обычными полями кладёте поле с CSS-скрытием:
<div class="form-row hp-trap" aria-hidden="true">
<label for="website">Ваш сайт</label>
<input type="text" name="website" id="website" tabindex="-1" autocomplete="off">
</div>
В CSS:
.hp-trap { position: absolute; left: -9999px; opacity: 0; height: 0; }
В обработчике формы (mail.php или серверная часть) проверяете:
if (!empty($_POST['website'])) {
http_response_code(200);
exit; // молча отклоняем, не показываем ботам, что мы их раскусили
}
Что важно:
- Не использовать display:none или visibility:hidden в CSS. Современные боты знают эти признаки и пропускают такие поля. Position absolute со смещением за экран обходят меньше ботов.
- Не помечать поле hidden type=»hidden». Это отдельный признак, который умные боты тоже пропускают.
- Имя поля должно быть нейтральным: website, url, email_confirm, address. Боты заполняют по совпадению имени, и если имя «обычное» рекламно-полезное, его заполнят с большой вероятностью.
Honeypot отсекает 70-80 процентов спама на маленьких сайтах. Никакого удобства для пользователя не теряется: реальный человек поле просто не видит.
Time-trap: проверка времени заполнения
Бот отправляет форму за 0.3 секунды после загрузки. Реальный человек минимум 5 секунд, обычно 30-90 секунд. Если форма отправлена слишком быстро, это бот.
Как делается. В скрытом поле формы при рендеринге кладёте текущий timestamp:
<input type="hidden" name="form_started" value="<?= time() ?>">
В обработчике сравниваете:
$elapsed = time() - intval($_POST['form_started'] ?? 0);
if ($elapsed < 3) {
http_response_code(200);
exit;
}
if ($elapsed > 3600) {
http_response_code(200);
exit;
}
Окно от 3 секунд (никто не заполняет быстрее) до часа (форма откроется, никто не подходит, бот заходит позже на ту же страницу).
Этот приём режет ещё 10-15 процентов оставшихся ботов, в основном простые curl-скрипты, которые не имитируют поведение пользователя.
Проверка referer и Origin
Боты часто отправляют POST-запрос напрямую на эндпоинт обработки, не открывая саму страницу с формой. У такого запроса нет заголовка Referer или он не совпадает с вашим доменом.
$ref = $_SERVER['HTTP_REFERER'] ?? '';
$origin = $_SERVER['HTTP_ORIGIN'] ?? '';
$allowed = ['https://saitfix.ru', 'https://www.saitfix.ru'];
$ok = false;
foreach ($allowed as $a) {
if (strpos($ref, $a) === 0 || strpos($origin, $a) === 0) {
$ok = true;
break;
}
}
if (!$ok) {
http_response_code(200);
exit;
}
Не самая надёжная защита: легко подделывается заголовком в curl. Но добавляет ещё одну проверку, через которую бот должен пройти. Простые боты не утруждаются.
CSRF-токен в сессии
Серьёзная мера: при рендере страницы кладёте в сессию случайный токен, в форму выводите его как hidden-поле. При обработке проверяете совпадение.
session_start();
$token = bin2hex(random_bytes(16));
$_SESSION['csrf'] = $token;
?>
<input type="hidden" name="csrf" value="<?= $token ?>">
В обработчике:
session_start();
if (empty($_POST['csrf']) || $_POST['csrf'] !== ($_SESSION['csrf'] ?? '')) {
http_response_code(200);
exit;
}
unset($_SESSION['csrf']);
Бот должен сначала открыть страницу, забрать токен, отправить вместе с формой. Это уже не curl-однострочник. Для нацеленных на ваш сайт ботов всё ещё проходимо, для общих сетевых ферм нет.

Контент-фильтры
Что точно не должно быть в нормальной заявке от человека:
- Несколько ссылок в сообщении. Особенно с tld
.xyz,.top,.click,.bid. Реальный человек редко вставляет ссылки в форму обращения. - Кириллица + латиница в одном слове (а не в одной фразе). Признак подмены символов в спам-словах: «казинo», «бeз дeпoзитa».
- Слова из чёрного списка. Я веду собственный мини-список: казино, ставки, наркотик, ветеринарка, сео-услуги, разработка сайта, нужны клиенты. Если три слова из списка совпали, в спам.
- Сообщение длиной меньше 10 символов. «Привет», «test», просто пробелы.
- Сообщение длиной больше 5000 символов. Реально длинные обращения редкость, обычно это либо ошибочный copy-paste, либо спам-лонгрид.
- Email из домена @example.com или @test.com.
Простой пример фильтра в php:
$msg = $_POST['message'] ?? '';
$lc = mb_strtolower($msg);
$blacklist = ['казино', 'ставки', 'crypto', 'invest', 'binance', 'forex'];
$hits = 0;
foreach ($blacklist as $w) {
if (mb_strpos($lc, $w) !== false) $hits++;
}
if ($hits >= 2) {
http_response_code(200);
exit;
}
if (substr_count($msg, 'http') > 2) {
http_response_code(200);
exit;
}
Этот фильтр режет полуручной спам, до которого honeypot и time-trap не достают.
Простая локальная капча
Если уж совсем хочется визуальной задачи для пользователя, делайте её локальной, без внешних зависимостей.
Математическая капча. «Сколько будет два плюс три». Бесплатно, не передаёт никаких данных, режет почти всех ботов. Минус: доступность для людей с когнитивными особенностями.
$a = rand(1, 9);
$b = rand(1, 9);
$_SESSION['captcha'] = $a + $b;
?>
<label>Сколько будет <?= $a ?> + <?= $b ?>?</label>
<input type="text" name="captcha">
Логическая капча. «Выберите ту картинку, на которой кошка». Сложнее в реализации, но без поля ввода для пользователя.
hCaptcha и Cloudflare Turnstile. Альтернативы reCAPTCHA, не от Google. Cloudflare Turnstile часто проходит без визуальной задачи, на основе анализа поведения. Из России Cloudflare работает нестабильно с лета 2024, поэтому на чисто русском сайте я предпочитаю hCaptcha (работает) или вообще обходиться без неё.
Лимит запросов с одного IP
В обработчике формы пишете в файл (или в Redis, если есть) количество запросов с конкретного IP. Если за минуту больше 3, или за час больше 20, отказываете.
$ip = $_SERVER['REMOTE_ADDR'];
$file = "/tmp/ratelimit/" . hash('sha256', $ip);
if (file_exists($file) && filesize($file) > 100) {
$lines = file($file);
$recent = array_filter($lines, fn($l) => intval($l) > time() - 60);
if (count($recent) >= 3) {
http_response_code(429);
exit;
}
}
file_put_contents($file, time()."n", FILE_APPEND);
Это не панацея, потому что современные боты имеют пул из тысяч IP. Но против локальных нагрузочных скриптов работает.
Теневое отбрасывание спама
Важный приём: пойманного бота не нужно отправлять в редирект на «вы спамер» или возвращать ошибку. Так он узнает, что попался, и прокачает свою тактику.
Возвращайте HTTP 200 OK и тот же шаблон «спасибо за обращение, мы свяжемся». Бот уйдёт довольный, на ваш email ничего не придёт.
В обработчике это выглядит как ранний выход с тем же финальным скриптом, что и при успехе:
if ($botDetected) {
include 'thanks.html';
exit;
}
Логирование подозрительных запросов
Чтобы потом смотреть, что у вас ловится:
function log_spam($reason, $data) {
$line = date('c') . " | $reason | " . json_encode($data) . "n";
file_put_contents('/var/log/saitfix/spam.log', $line, FILE_APPEND);
}
Раз в неделю заглядывайте в этот лог и смотрите, что фильтруется. Иногда видно паттерн, на который можно добавить новое правило. Иногда видно, что вы по ошибке режете реальных людей.

Что я ставлю на каждом клиентском проекте
Сводный шаблон, который у меня лежит в mail.php готовым:
- Honeypot скрытое поле website, любое заполнение заворачиваем.
- Time-trap окно от 3 секунд до часа.
- Referer/Origin check запросы только с собственных доменов.
- CSRF-токен для длинных форм с несколькими шагами.
- Контент-фильтр черный список из 30 слов плюс правила про ссылки и длину.
- Rate-limit не больше 5 заявок с одного IP в час.
- Логирование всех отказанных в
/var/log/<domain>/spam.log. - Сценарий теневого ответа все боты получают «спасибо», но письмо не уходит.
После такого набора у клиентов поток спама падает с десятков в день до 1-2 в неделю. И эти 1-2 проходят полуручную фильтрацию, против которой техники бессильны.
Без reCAPTCHA, без Google, без передачи данных в США, без замедления страницы.
Что почитать дальше
Связанные материалы: Файл .htaccess против ботов и парсеров, Как ограничить число попыток входа в админку, Согласие на обработку персональных данных.
Если в вашей форме всё это надо настроить, и руками лезть не хочется, у меня в годовом и месячном тарифе абонентского обслуживания такая защита идёт сразу. Заявку на подключение оставить через форму на главной (она, кстати, защищена ровно по этому шаблону).
Частые вопросы
Как защитить форму от спама без капчи
Скрытым полем, которое человек не видит, а бот заполняет, проверкой времени заполнения и проверкой источника запроса. Вместе они убирают подавляющую часть мусора.
Что такое скрытое поле-ловушка
Обычное поле формы, спрятанное стилями. Человек его не видит и не заполняет, а бот заполняет все поля подряд. Если поле не пустое, заявка отбрасывается.
Почему проверка времени работает
Потому что бот отправляет форму мгновенно, а человек тратит на заполнение хотя бы несколько секунд. Отправка быстрее этого порога почти всегда автоматическая.
Чем плоха reCAPTCHA
Замедляет страницу, передаёт данные посетителей за рубеж и раздражает людей заданиями с картинками. Для маленького сайта она избыточна: перечисленных мер хватает.