WPForms не сохраняет отправку при возврате на страницу: как убрать повторную отправку и дубли

Сценарий знакомый: пользователь отправил форму, потом нажал «назад», обновил страницу или вернулся к форме из истории браузера — и WordPress снова принимает отправку. В админке появляются дубли, а в CRM улетают повторные лиды. Это уже не проблема доставки письма и не ошибка AJAX, а отдельная задача: нужно сделать повторную отправку невозможной или хотя бы безопасной.

Ниже — практический разбор для WPForms: где искать причину, что можно исправить настройками, а где уже нужен небольшой код.

Когда это действительно проблема дублирования

Сначала стоит отделить повторную отправку от обычного двойного клика. Если форма уходит дважды сразу после нажатия кнопки, причина чаще в JS, кеше или конфликте скриптов. Если же дубль появляется после возврата на страницу, обновления или открытия формы из истории браузера, значит форма не блокируется после успешной отправки на уровне страницы или сессии.

Типичные признаки

  • в записи WPForms одна и та же почта или телефон встречаются несколько раз подряд;
  • после отправки пользователь видит страницу с формой, а не отдельное подтверждение;
  • кнопка «Назад» возвращает уже заполненную форму, и повторный submit проходит без предупреждения;
  • дубли появляются не у всех, а только у части пользователей — обычно у тех, кто возвращается в браузере назад.

Диагностика: что проверить до правок

Не начинайте с кода. Сначала проверьте, как именно ведет себя форма в чистом сценарии. Это помогает понять, где ломается логика: в шаблоне, в кэше, в редиректе или в самой форме.

  1. Откройте страницу формы в режиме инкогнито.
  2. Отправьте тестовую заявку.
  3. Нажмите «Назад» в браузере и посмотрите, что отображается: старая форма, страница подтверждения или пустой экран.
  4. Обновите страницу и попробуйте отправить еще раз.
  5. Если используется кеш-плагин, временно отключите кеш для этой страницы и повторите тест.

Если после отправки пользователь остается на той же странице с формой, браузер почти всегда позволяет повторный submit. Если же форма уходит на отдельную страницу «спасибо», риск дублей ниже, но он все равно остается, если страница ответа доступна через историю браузера.

Пошаговое решение

1. Переведите форму на отдельную страницу подтверждения

Самый надежный вариант — после успешной отправки не оставлять пользователя на форме. В WPForms для этого используйте редирект на страницу благодарности или отдельный текст подтверждения без самой формы. Это не убирает проблему полностью, но сильно снижает шанс повторной отправки через обновление.

Если редирект уже настроен, проверьте, что страница подтверждения не содержит ту же форму и не подгружается из кеша вместе с ней.

2. Отключите кеширование для страницы с формой, если оно мешает логике

Если страница формы отдается из агрессивного кеша, пользователь может получить старое состояние формы даже после успешной отправки. Для таких страниц обычно лучше исключить их из кеша или хотя бы не кешировать HTML-ответ, если форма критична для бизнеса.

Это особенно важно, если вы используете:

  • страницы с одноразовыми заявками;
  • формы регистрации или бронирования;
  • формы, которые создают запись в CRM или в пользовательской таблице.

3. Добавьте защиту от повторной отправки на уровне сессии

Если нужно именно запретить повторную отправку после первого успешного submit, можно сохранить флаг в PHP-сессии или в cookie и проверять его перед отправкой. Для WordPress это рабочий путь, если вы контролируете шаблон и понимаете, что делаете.

Ниже пример, который блокирует повторную отправку формы с конкретным ID в течение одной сессии браузера:

<?php
add_action( 'init', function () {
    if ( ! session_id() && ! headers_sent() ) {
        session_start();
    }
} );

add_filter( 'wpforms_process_before', function( $fields, $entry, $form_data ) {
    $target_form_id = 123; // замените на ID своей формы

    if ( (int) $form_data['id'] !== $target_form_id ) {
        return $fields;
    }

    $session_key = 'wpforms_submitted_' . $target_form_id;

    if ( ! empty( $_SESSION[ $session_key ] ) ) {
        wp_die( 'Эта форма уже была отправлена в текущей сессии.' );
    }

    return $fields;
}, 10, 3 );

add_action( 'wpforms_process_complete', function( $fields, $entry, $form_data ) {
    $target_form_id = 123;

    if ( (int) $form_data['id'] !== $target_form_id ) {
        return;
    }

    if ( ! session_id() && ! headers_sent() ) {
        session_start();
    }

    $_SESSION['wpforms_submitted_' . $target_form_id ] = time();
}, 10, 3 );

Это решение не универсальное: сессии могут конфликтовать с некоторыми кешами и прокси. Но для закрытого сценария, где важнее не допустить дубль, чем сохранить возможность многократной отправки, оно подходит.

4. Для публичных форм используйте одноразовый токен

Если форма должна быть доступна повторно, но не должна принимать одинаковую отправку дважды, лучше использовать одноразовый токен. Логика простая: при загрузке формы генерируется токен, при отправке он проверяется и помечается как использованный. Это надежнее, чем просто блокировать по сессии.

<?php
add_filter( 'wpforms_frontend_form_data', function( $form_data ) {
    if ( (int) $form_data['id'] !== 123 ) {
        return $form_data;
    }

    $token = wp_generate_password( 20, false, false );
    set_transient( 'wpforms_token_' . $token, 1, HOUR_IN_SECONDS );

    $form_data['fields'][999] = array(
        'id'       => 999,
        'type'     => 'hidden',
        'label'    => 'Token',
        'default'  => $token,
        'required' => '1',
    );

    return $form_data;
} );

add_action( 'wpforms_process_before', function( $fields, $entry, $form_data ) {
    if ( (int) $form_data['id'] !== 123 ) {
        return $fields;
    }

    $token = isset( $_POST['wpforms']['fields'][999] ) ? sanitize_text_field( wp_unslash( $_POST['wpforms']['fields'][999] ) ) : '';

    if ( ! $token || ! get_transient( 'wpforms_token_' . $token ) ) {
        wp_die( 'Повторная отправка формы заблокирована.' );
    }

    delete_transient( 'wpforms_token_' . $token );

    return $fields;
}, 10, 3 );

Здесь важно понимать ограничение: добавление скрытого поля через фильтр зависит от того, как именно собрана форма и как вы выводите поля. Если не хотите вмешиваться в структуру формы, проще сделать блокировку на уровне редиректа и подтверждения.

Сравнение подходов

ПодходКогда подходитМинус
Редирект на страницу спасибоПочти всегда как базовая мераНе защищает от всех повторов
Отключение кеша для страницы формыЕсли проблема связана со старым HTMLМожет ухудшить производительность
Сессия / cookieНужно запретить повтор в рамках визитаНе подходит для всех конфигураций кеша
Одноразовый токенНужна более строгая защита от дублейТребует аккуратной реализации

Как проверить, что решение сработало

После внедрения не ограничивайтесь одной тестовой отправкой. Проверьте именно проблемный сценарий: возврат назад, обновление страницы, повторное открытие из истории браузера.

  • Отправьте форму один раз.
  • Вернитесь назад и попробуйте нажать submit повторно.
  • Обновите страницу и проверьте, появляется ли та же форма в прежнем состоянии.
  • Посмотрите в WPForms Entries, не создалась ли вторая запись.
  • Если форма отправляет данные в CRM, проверьте, не появился ли дубль там.

Если вы используете редирект, убедитесь, что после возврата назад браузер не показывает старую форму в состоянии, которое позволяет отправку без нового ввода. Если используете токен, проверьте, что повторная попытка получает отказ, а не создает новую запись.

Частые ошибки и как их исправить

Форма остается на той же странице после успеха

Это самая частая причина дублей. Исправление простое: настройте редирект или отдельную страницу подтверждения. Если редирект уже есть, проверьте, не перекрывает ли его тема или другой плагин.

Кешируетcя страница с формой

Если HTML страницы отдается из кеша, пользователь может видеть устаревшее состояние формы. Исключите страницу из кеша на уровне плагина или сервера. Для динамических форм это часто обязательная мера.

Сессия не стартует или ломает заголовки

Если используете PHP-сессию, не запускайте ее после вывода HTML. Иначе получите предупреждения и нестабильное поведение. Сессию нужно стартовать на раннем хуке, до отправки заголовков.

Проверка по cookie слишком слабая

Cookie можно удалить, а в некоторых сценариях они не подходят для строгой защиты. Если форма критична, лучше использовать одноразовый токен или серверную проверку по записи.

Пытаются лечить проблему только JavaScript-ом

Отключить кнопку после клика полезно, но этого недостаточно. Пользователь может обновить страницу, открыть ее в другой вкладке или отправить запрос вручную. Клиентская блокировка — это только дополнительный слой.

Что делать для безопасности и производительности

Не стоит превращать защиту от дублей в тяжелую систему, если задача простая. Для большинства сайтов достаточно редиректа после отправки и исключения страницы формы из кеша. Если форма используется для заявок, где дубль критичен, добавляйте серверную проверку, но не храните лишние данные дольше, чем нужно.

  • не храните токены вечно — используйте ограниченный TTL;
  • не полагайтесь только на фронтенд-блокировку кнопки;
  • не включайте агрессивный кеш на страницы с динамическими формами;
  • проверяйте, не дублирует ли отправку сторонняя интеграция — CRM, webhook, email-автоматизация.

Если вам нужно еще и почистить сайт от лишнего технического шума, убрать дубли и сократить количество лишних скриптов, в экосистеме WPShop есть Clearfy Pro: https://wpshop.ru/plugins/clearfy. Но саму логику повторной отправки он не заменяет — это именно задача формы и маршрута после submit.

Если коротко: сначала уберите повторную отправку на уровне сценария пользователя, потом закрепите это редиректом или серверной проверкой. Так вы закроете дубли без лишней нагрузки на сайт и без ложных срабатываний для нормальных посетителей.