Форма обратной связи есть на 90% сайтов. И на 90% из них летит спам. Разберём, как сделать форму, которая защищена от ботов, но не бесит пользователей капчей.
Что нужно форме
- Валидация на клиенте (HTML5-атрибуты)
- Валидация на сервере (PHP)
- Защита от CSRF
- Защита от спама (honeypot + rate limit)
- Сохранение в БД
- Уведомление админу
HTML-форма
<form method="post" action="/contact-handler.php">
<input type="text" name="name" required maxlength="100">
<input type="email" name="email" required maxlength="150">
<textarea name="message" required maxlength="5000"></textarea>
<!-- Honeypot: спрятанное поле -->
<div style="position:absolute; left:-9999px;">
<input type="text" name="website" tabindex="-1" autocomplete="off">
</div>
<button type="submit">Отправить</button>
</form>
1. Honeypot — защита от ботов
Идея: у формы есть скрытое поле website. Пользователь его не видит и не заполняет. Бот — видит все поля в HTML и заполняет их.
if (!empty($_POST['website'])) {
// Это бот — тихо делаем вид что всё ок
header('Location: /contact/?sent=1');
exit;
}
Плюс: невидимая капча, работает автоматически. Минус: продвинутые боты тоже это распознают.
2. Rate limit по IP
Не больше N отправок за период с одного IP:
$stmt = $pdo->prepare("
SELECT COUNT(*) FROM contact_messages
WHERE ip = ? AND created_at > NOW() - INTERVAL 1 HOUR
");
$stmt->execute([$ip]);
if ((int)$stmt->fetchColumn() >= 5) {
header('Location: /contact/?err=rate');
exit;
}
5 сообщений в час — нормально для человека. Для спам-бота — стоп.
3. Проверка длины сообщения
Спам часто содержит 1-2 слова: «Купите крипту», «Ссылка на сайт». Отсекаем:
if (mb_strlen($message) < 10) {
$errors[] = 'Сообщение слишком короткое';
}
4. Проверка email
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$errors[] = 'Неверный email';
}
Плюс — можно проверить MX-записи домена, чтобы отсеять одноразовые email:
$domain = substr(strrchr($email, "@"), 1);
if (!checkdnsrr($domain, 'MX')) {
$errors[] = 'Домен email не существует';
}
5. Blacklist слов
Список типовых спам-слов:
$blacklist = ['viagra', 'casino', 'кредит', 'заработок', 'крипта', 'биткоин'];
foreach ($blacklist as $word) {
if (stripos($message, $word) !== false) {
// Возможно спам
$flag = true;
break;
}
}
6. Сохранение в БД
$stmt = $pdo->prepare("
INSERT INTO contact_messages (name, email, subject, message, ip, user_agent)
VALUES (?, ?, ?, ?, ?, ?)
");
$stmt->execute([
mb_substr($name, 0, 100),
mb_substr($email, 0, 150),
mb_substr($subject, 0, 200),
mb_substr($message, 0, 5000),
$ip,
mb_substr($_SERVER['HTTP_USER_AGENT'] ?? '', 0, 500),
]);
Важно: все поля через mb_substr — чтобы не вставить мегабайт текста.
7. Уведомление админу
Email — простое решение:
$headers = "From: Site <[email protected]>\r\n" . "Reply-To: $email\r\n" . "Content-Type: text/plain; charset=UTF-8\r\n"; @mail($admin_email, $subject, $body, $headers);
Или через Telegram-бот — работает надёжнее, чем email.
8. Редирект после отправки (PRG)
Паттерн Post-Redirect-Get — после отправки формы делаем редирект. Иначе F5 переотправит форму, и создастся дубликат.
header('Location: /contact/?sent=1');
exit;
Что даёт комплексная защита
- Без honeypot — 100+ спам-сообщений в день
- С honeypot — 10-20 в день
- + rate limit — 1-3 в день
- + blacklist — 0-1 в день
Комбинация этих техник работает лучше, чем капча от Google. Плюс — не бесит пользователей.
Что НЕ делать
- ❌ Простая арифметическая капча — боты обходят за час
- ❌ ReCAPTCHA v2 — уменьшает конверсию на 20-40%
- ❌ Подтверждение email перед отправкой — теряет 50% заявок
- ❌ Капча для всех — отсекает реальных клиентов
Капча — крайняя мера. Сначала honeypot + rate limit + blacklist. Если этого мало — тогда капча, но только для подозрительных.