WPForms showing submission and reCAPTCHA errors

WPForms Lite + reCAPTCHA v3: pusty token po optymalizacji JS w WP-Optimize

Formularz kontaktowy działał wcześniej normalnie, ale po zmianach związanych z optymalizacją JavaScript zaczął odrzucać wysłanie z błędem Google reCAPTCHA. Sam komunikat nie mówił jeszcze, czy problemem jest klucz reCAPTCHA, próg punktacji, zgoda cookies czy kod formularza. Dopiero Network w DevTools pokazał rzecz, która naprawdę zawęziła diagnozę: żądanie do wp-admin/admin-ajax.php wracało z HTTP 200, ale JSON miał success:false, a pole wpforms[recaptcha] w wysyłanym formularzu było puste.

WPForms pokazujący błąd wysłania i błąd reCAPTCHA podczas testu formularza
Objaw po stronie formularza: wysłanie nie przechodzi, a WPForms raportuje błąd reCAPTCHA.

Środowisko, w którym wystąpił problem

Ten przypadek dotyczy konkretnej instalacji, dlatego nie traktuję go jako stwierdzenia, że WP-Optimize zawsze psuje WPForms. W chwili diagnozy używałem WordPress 7.1, PHP 8.5.7, WPForms Lite 2.0.1.1, WP-Optimize 4.6.1, Complianz 7.5.4 i Polylang 3.8.7. W WPForms działał Google reCAPTCHA v3 z progiem 0,4, a No-Conflict Mode był wyłączony.

Ustawienia WPForms z wybranym Google reCAPTCHA v3
WPForms korzystał z Google reCAPTCHA v3.
Ustawienia WPForms z progiem reCAPTCHA 0,4 i wyłączonym No-Conflict Mode
Próg wynosił 0,4, a No-Conflict Mode był wyłączony. Te ustawienia nie okazały się przyczyną opisywanego problemu.

HTTP 200 nie oznacza, że formularz został wysłany

Najważniejszy krok diagnostyczny to nie zatrzymywać się na statusie HTTP. W DevTools otworzyłem Network, odnalazłem request do admin-ajax.php i sprawdziłem treść odpowiedzi. Serwer odpowiadał HTTP 200, ale payload WPForms zawierał success:false oraz błąd reCAPTCHA. Transport HTTP zadziałał, natomiast sama operacja biznesowa formularza zakończyła się błędem.

Następnie sprawdziłem dane multipart wysyłane przez formularz. Pole wpforms[recaptcha] istniało, ale jego wartość była pusta. To był znacznie lepszy trop niż sam tekst błędu. W reCAPTCHA v3 JavaScript musi wygenerować token w momencie chronionej akcji i przekazać go do backendu. Jeśli pole przeznaczone na token jest puste, backend nie ma czego prawidłowo zweryfikować.

Content-Disposition: form-data; name="wpforms[recaptcha]"

# podczas FAIL:
# wartość pusta

Dlaczego nie obwiniłem od razu Complianz

Na stronie reCAPTCHA jest objęta mechanizmem zgody. W Network było jednak widać, że po zgodzie ładowały się skrypty Google oraz iframe reCAPTCHA. Wykonałem też test Accept → reload → submit i formularz nadal nie przechodził. To nie wyklucza każdego możliwego problemu z consentem w innych konfiguracjach, ale w tym konkretnym incydencie nie tłumaczyło pustego tokena.

Test A/B: wyłączenie Minify i Merge

Kolejny krok był celowo prosty i odwracalny. W WP-Optimize wyłączyłem jednocześnie:

  • Enable minification of JavaScript files,
  • Enable merging of JavaScript files.

Po zapisaniu ustawień wyczyściłem cache Minify i Page Cache, a test zrobiłem ponownie. Formularz przeszedł dwa razy z rzędu. To wystarczyło, żeby zawęzić problem do warstwy przetwarzania JavaScript na tej instalacji.

Ważne ograniczenie: nie zrobiłem wtedy osobnego testu „tylko Minify OFF” oraz „tylko Merge OFF”. Nie twierdzę więc, że pojedynczą przyczyną była wyłącznie minifikacja albo wyłącznie scalanie. Potwierdzony jest konflikt przy aktywnym zestawie ustawień, a test izolacyjny wyłączał obie funkcje razem.

Docelowy fix: zachować optymalizację, wykluczyć tylko WPForms

Nie chciałem zostawiać całego przetwarzania JavaScript wyłączonego. Lepszym rozwiązaniem było ponowne włączenie Minify i Merge oraz wykluczenie konkretnego skryptu WPForms Lite w:

WP-Optimize → Minify → JavaScript → Exclude JavaScript from processing

/wp-content/plugins/wpforms-lite/assets/js/frontend/wpforms.min.js

Na tej stronie istniało już osobne wykluczenie dla Post Views Counter, więc finalna lista zawierała:

/wp-content/plugins/post-views-counter/js/frontend.js
/wp-content/plugins/wpforms-lite/assets/js/frontend/wpforms.min.js

WP-Optimize oficjalnie pozwala wykluczać pojedyncze pliki JavaScript z minifikacji i scalania. To jest właśnie typ naprawy, który preferuję: pozostawić optymalizację włączoną dla reszty strony, a problematyczny asset ładować bez przetwarzania.

False lead: poprawna nazwa wtyczki ma znaczenie w ścieżce

Po drodze użyłem ścieżki:

/wp-content/plugins/wpforms/assets/js/frontend/wpforms.min.js

To wygląda sensownie i podobną ścieżkę można spotkać w dokumentacji dla innych wariantów WPForms, ale na tej instalacji WPForms Lite była to zła ścieżka. Rzeczywisty asset ładował się z katalogu wpforms-lite.

WP-Optimize z błędną ścieżką wpforms zamiast wpforms-lite w wykluczeniach JavaScript
False lead, nie finalna konfiguracja: początkowo wpisałem /wpforms/.... W tej instalacji właściwy katalog to /wpforms-lite/....

Nie warto więc kopiować exclusion line bez sprawdzenia faktycznego URL skryptu w Network albo w źródle strony. Nazwa katalogu może zależeć od wariantu i wersji wtyczki.

Purge i test w świeżej sesji są częścią naprawy

Po zmianie exclusion zrobiłem purge Minify i Page Cache, a potem testy w świeżej prywatnej sesji. To ważne, bo w czasie przejścia pojawił się jeszcze pojedynczy przypadek pustego tokena w EN, zanim końcowe testy zaczęły przechodzić stabilnie. Sam wpis w polu exclusions nie jest więc dla mnie końcem diagnostyki.

Finalnie normalne wysłanie formularza zostało potwierdzone w PL, EN i DE.

Potwierdzenie poprawnego wysłania formularza WPForms po zmianie konfiguracji
Po zmianie konfiguracji i ponownym QA formularz przechodził prawidłowo.

Jak zweryfikować fix krok po kroku

  1. Wyczyść cache Minify oraz Page Cache.
  2. Otwórz nowe prywatne okno przeglądarki.
  3. Ustaw consent zgodnie z używanym CMP.
  4. Wyślij formularz z realnymi danymi testowymi.
  5. W Network otwórz request do admin-ajax.php.
  6. Sprawdź JSON — nie tylko HTTP status.
  7. Jeśli formularz nadal nie przechodzi, sprawdź multipart i upewnij się, że wpforms[recaptcha] jest obecne i niepuste.
  8. Sprawdź komunikat potwierdzenia oraz faktyczne dostarczenie wiadomości.

Co nie było przyczyną w tym przypadku

W trakcie diagnozy sprawdziłem kilka rozsądnych tropów, ale żaden z nich nie wyjaśniał pustego tokena:

  • próg reCAPTCHA 0,4 — nie został potwierdzony jako przyczyna;
  • No-Conflict Mode — nie był potrzebny do finalnego rozwiązania;
  • sam mechanizm zgody Complianz — Accept → reload → submit nadal kończył się błędem;
  • globalny tekst błędu WPForms — wpływał na lokalizację komunikatu, nie na generację tokena;
  • błędna ścieżka /wpforms/... — po prostu nie wskazywała właściwego assetu Lite.

Drobna pułapka z lokalizacją komunikatu reCAPTCHA

Na początku wpisałem własny polski tekst w globalnym polu Fail Message w ustawieniach CAPTCHA. Efekt uboczny był prosty: ten sam polski tekst pojawiał się również w wersji EN. Po przywróceniu standardowego komunikatu WPForms lokalizacja działała natywnie: EN po angielsku, PL po polsku, a backend DE zwracał komunikat niemiecki.

Domyślne pole Fail Message w ustawieniach reCAPTCHA WPForms
Globalnego pola Fail Message nie używam do ręcznego tłumaczenia każdej wersji językowej.
Porównanie natywnie zlokalizowanych błędów reCAPTCHA w angielskim i polskim formularzu
Po powrocie do defaultu WPForms komunikaty EN i PL lokalizowały się natywnie.

Osobny bug w niemieckim formularzu

Podczas testu z celowo niespełnioną weryfikacją reCAPTCHA zauważyłem jeszcze osobny problem. Backend zwracał po niemiecku zarówno ogólny nagłówek błędu, jak i szczegółowy błąd reCAPTCHA, ale frontend pokazywał tylko nagłówek. Problem odtworzyłem w nowym prywatnym oknie. Normalny niemiecki submit po zgodzie działał, więc nie traktuję tego jako przyczyny pustego tokena. Zgłosiłem ten przypadek osobno na forum WordPress.org.

Niemiecki formularz WPForms pokazujący ogólny błąd oraz odpowiedź JSON zawierającą dodatkowy błąd reCAPTCHA
Osobny finding DE: backend zwraca szczegółowy błąd reCAPTCHA, ale frontend go nie renderuje.

Rollback

Jeśli exclusion powoduje nieoczekiwany efekt, rollback jest prosty: usuń wpis WPForms z listy wykluczeń, zapisz ustawienia i wyczyść cache. Do diagnozy można też tymczasowo wyłączyć Minify i Merge, ale nie ma powodu wyłączać całego WP-Optimize ani page cache na stałe, jeśli problem dotyczy tylko jednego assetu.

Wniosek

Najbardziej użyteczny wniosek z tego incydentu nie brzmi „wyłącz optymalizację”, tylko „sprawdź, co naprawdę trafia do requestu”. W moim przypadku HTTP 200 maskował błąd aplikacyjny, a puste wpforms[recaptcha] wskazało problem znacznie szybciej niż kolejne zmiany progu, tłumaczeń czy ustawień CAPTCHA. Dopiero potem test A/B potwierdził warstwę JS optimization, a wąskie exclusion pozwoliło zachować Minify i Merge dla reszty strony.

Pomogła Ci ta instrukcja?

Jeśli zaoszczędziła Ci trochę czasu albo nerwów, możesz postawić mi kawę.