Как сделать форму обратной связи на PHP с антиспамом

Kuznya Studio 18.09.2026 1

Форма обратной связи есть на 90% сайтов. И на 90% из них летит спам. Разберём, как сделать форму, которая защищена от ботов, но не бесит пользователей капчей.

Что нужно форме

  1. Валидация на клиенте (HTML5-атрибуты)
  2. Валидация на сервере (PHP)
  3. Защита от CSRF
  4. Защита от спама (honeypot + rate limit)
  5. Сохранение в БД
  6. Уведомление админу

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. Если этого мало — тогда капча, но только для подозрительных.