Cloudflare Turnstile: jak działa, co widzi po Twojej stronie i jak go podłączyć — przewodnik krok po kroku
Spis treści
- Wprowadzenie: co da ci ten przewodnik
- Przygotowanie wstępne: narzędzia i dostępy
- Podstawowe pojęcia: jak zbudowany jest cloudflare turnstile
- Krok 1: zrozum, co dokładnie turnstile widzi po twojej stronie
- Krok 2: tworzymy widget w panelu cloudflare
- Krok 3: wbudowujemy widget na stronę z formularzem
- Krok 4: konfigurujemy serwerową weryfikację tokenu
- Krok 5: testujemy widget we wszystkich trybach z kluczami testowymi
- Krok 6: patrzymy na turnstile oczami odwiedzającego przez proxy mobilne
- Sprawdzenie wyniku: końcowa lista kontrolna
- Typowe błędy i ich rozwiązania
- Dodatkowe możliwości dla zaawansowanych
- Faq: częste pytania o cloudflare turnstile
- Zakończenie
Wprowadzenie: co da Ci ten przewodnik
Cloudflare Turnstile to ten mały widget z kręcącym się kółkiem i napisem „Verifying”, który coraz częściej pojawia się w miejscu znanej captchy z obrazkami. Stoi na formularzach rejestracji, w koszykach sklepów internetowych, na landing page'ach i w panelach klienta różnych serwisów. Zwykły odwiedzający prawie go nie zauważa. Ale dla marketera, arbitrażowca czy dewelopera, który pracuje przez proxy mobilne i otwiera dziesiątki stron dziennie, staje się zagadką. Dlaczego na jednym profilu weryfikacja przechodzi w sekundę, a na innym widget się zastanawia, prosi o kliknięcie checkboxa albo zwraca kod błędu?
W tym przewodniku rozbierzemy Cloudflare Turnstile z dwóch stron. Ze strony właściciela strony — samodzielnie podłączysz widget na stronę, skonfigurujesz weryfikację serwerową i nauczysz się czytać statystyki. Ze strony odwiedzającego — zrozumiesz, jakie dokładnie sygnały zbiera Turnstile, co widzi o Twojej przeglądarce, sieci i proxy oraz dlaczego podejmuje taką czy inną decyzję. Ta wiedza jest równie przydatna tym, którzy chronią swoje formularze przed botami, jak i tym, którzy chcą, żeby ich profile robocze wyglądały dla weryfikacji jak zwykli użytkownicy.
Dla kogo jest ten przewodnik
- Dla właścicieli firm i marketerów, którym spamowe zgłoszenia i fałszywe rejestracje psują statystyki i zjadają budżet.
- Dla deweloperów, którzy muszą szybko i bez błędów wbudować Turnstile w formularz i poprawnie weryfikować token na serwerze.
- Dla arbitrażowców i specjalistów od multiaccountingu, którzy pracują przez proxy mobilne i chcą rozumieć, co widzi Turnstile przy ich połączeniu.
Co warto wiedzieć wcześniej
Specjalistyczna wiedza nie jest potrzebna. Wystarczy rozumieć, czym jest strona HTML, umieć otworzyć konsolę deweloperską w przeglądarce i mieć choć minimalne doświadczenie z dowolnym językiem po stronie serwera — PHP, Node.js, Python. Jeśli nie masz części serwerowej, i tak przejdziesz większość przewodnika: widget można podłączyć i przetestować na lokalnej stronie.
Ile czasu będzie potrzebne
Pełne przejście — około półtorej do dwóch godzin. Rejestracja i utworzenie widgetu zajmują 10-15 minut, wbudowanie na stronę — 20 minut, weryfikacja serwerowa — 30-40 minut, testowanie i diagnostyka — jeszcze 30 minut. Rozdziały teoretyczne możesz czytać w dowolnej kolejności i wracać do nich w razie potrzeby.
Przygotowanie wstępne: narzędzia i dostępy
Zanim zaczniesz, zbierz wszystko, co potrzebne. To uchroni Cię od przerw w środku procesu.
Niezbędne narzędzia i dostępy
- Konto Cloudflare. Bezpłatne. Rejestruje się przez adres e-mail w kilka minut. Nie musisz przenosić domeny do Cloudflare — Turnstile działa na każdej stronie, gdziekolwiek jest hostowana.
- Strona lub strona testowa. Wystarczy dowolna strona HTML z formularzem. Do lokalnych eksperymentów wystarczy plik na komputerze otwarty przez prosty lokalny serwer.
- Środowisko serwerowe. Dowolny hosting z PHP albo Node.js lub Python na Twoim komputerze. Potrzebne do drugiej połowy przewodnika — weryfikacji tokenu.
- Nowoczesna przeglądarka. Chrome, Firefox, Edge lub Safari w aktualnej wersji z otwartymi narzędziami deweloperskimi.
- Proxy mobilne z możliwością zmiany IP. Przyda się w rozdziale diagnostycznym, żeby zobaczyć, jak widget reaguje na różne sieci i rotację adresów.
Wymagania systemowe
Turnstile nie jest wymagający. Widget działa w każdej przeglądarce, która obsługuje nowoczesny JavaScript, i ładuje swój kod z domeny challenges.cloudflare.com. Jeśli w Twojej sieci lub w rozszerzeniach przeglądarki ta domena jest zablokowana, widget się nie załaduje — miej to na uwadze przy testowaniu. Do weryfikacji serwerowej potrzebna jest możliwość wysyłania wychodzących zapytań HTTPS.
Co przygotować przed startem
- Załóż plik tekstowy na notatki. Będziesz w nim zapisywać site key, nazwę widgetu, listę hostname'ów i wyniki testów.
- Otwórz stronę z formularzem, który chcesz chronić, i zapisz jej kopię z datą. To Twoja kopia zapasowa.
- Jeśli masz serwerowy handler formularza, zrób kopię również jego. Będziemy do niego dodawać kod weryfikacji.
- Sprawdź, czy Twój lokalny lub produkcyjny serwer serwuje stronę przez HTTPS albo przez localhost. Turnstile działa też po zwykłym HTTP, ale na produkcyjnej stronie i tak potrzebujesz HTTPS.
⚠️ Uwaga: Klucza sekretnego widgetu nie można przechowywać w HTML, w JavaScripcie na stronie ani w publicznym repozytorium. Żyje wyłącznie na serwerze. Jeśli przez przypadek go opublikowałeś, natychmiast wygeneruj nowy klucz w panelu Cloudflare — stary przestanie działać.
Podstawowe pojęcia: jak zbudowany jest Cloudflare Turnstile
Żeby kolejne kroki były zrozumiałe, wyjaśnijmy kluczowe terminy prostym językiem.
Kluczowe terminy
- Widget — ten blok, który widzi odwiedzający. Technicznie to iframe załadowany z domeny Cloudflare i wbudowany w Twoją stronę.
- Site key — publiczny identyfikator widgetu. Wstawia się go w HTML i jest widoczny dla wszystkich. Po nim Cloudflare rozpoznaje, który widget wyrenderować i dla jakich domen jest dozwolony.
- Secret key — klucz prywatny. Nim Twój serwer potwierdza, że przyszedł prawdziwy token. Nigdy nie opuszcza serwera.
- Token — ciąg znaków, który widget wydaje po udanej weryfikacji. Trafia do ukrytego pola formularza i idzie na Twój serwer razem z pozostałymi danymi.
- Siteverify — endpoint Cloudflare, do którego serwer wysyła token razem z kluczem sekretnym i otrzymuje odpowiedź: sukces albo nie.
- Tryb widgetu — sposób wyświetlania: zarządzany (Managed), nieinteraktywny (Non-interactive) lub niewidoczny (Invisible). Różnica opisana jest poniżej.
- Hostname — domena, na której wolno używać widgetu. Jeśli domena nie jest wskazana, widget odmówi działania z błędem.
Zasada działania w czterech zdaniach
- Strona ładuje skrypt Turnstile, a widget po cichu uruchamia w przeglądarce zestaw sprawdzeń.
- Cloudflare zbiera wyniki, ocenia je razem z danymi o sieci i decyduje: przepuścić od razu, pokazać checkbox do potwierdzenia albo odmówić.
- Przy sukcesie widget generuje jednorazowy token i wstawia go do formularza.
- Twój serwer otrzymuje formularz, wysyła token do siteverify i tylko przy pozytywnej odpowiedzi przetwarza zgłoszenie.
Co sprawdza Turnstile — ogólny obraz
Tu ważne jest zrozumienie jednej rzeczy. Cloudflare Turnstile nie patrzy na „ludzkość” przez łamigłówki z obrazkami. Ocenia spójność środowiska: na ile przeglądarka, sieć i zachowanie składają się w jeden wiarygodny obraz. Główne grupy sygnałów:
- Środowisko przeglądarki. Skrypt wykonuje serię drobnych zadań JavaScript i sprawdza, że środowisko zachowuje się jak prawdziwa przeglądarka: jak zbudowane są obiekty okna, jak renderuje się grafika, czy są oznaki automatyzacji, czy zadeklarowany User-Agent zgadza się z rzeczywistymi możliwościami silnika.
- Dowód wykonanej pracy. Widget prosi przeglądarkę o wykonanie drobnego obliczenia. Dla człowieka to ułamki sekundy, dla bota otwierającego tysiące stron — odczuwalne obciążenie.
- Sygnały sieciowe. Reputacja adresu IP i systemu autonomicznego, z którego idzie zapytanie, zgodność cech sieciowych z zadeklarowaną przeglądarką, historia zapytań z tego adresu w całej sieci Cloudflare.
- Tokeny zaufania urządzenia. Na urządzeniach Apple i w niektórych innych ekosystemach Turnstile może zapytać system operacyjny o potwierdzenie, że ma przed sobą prawdziwe urządzenie — i wtedy weryfikacja przechodzi całkowicie bez obliczeń.
- Zachowanie na stronie. Czas pojawienia się widgetu, moment wysłania formularza, naturalność działań w trybie zarządzanym.
Czego Turnstile nie robi: nie zbiera danych do profilowania reklamowego, nie śledzi użytkownika między stronami przez cookie firm trzecich i nigdy nie pokazuje puzzli z obrazkami. To ważne zarówno z punktu widzenia prawa o danych osobowych, jak i konwersji — odwiedzający nie odchodzą z powodu irytującej captchy.
Trzy tryby widgetu
- Managed — tryb domyślny. Widget jest widoczny, kręci wskaźnikiem, a jeśli ma wątpliwości, pokazuje checkbox, który trzeba kliknąć. Pasuje do większości formularzy.
- Non-interactive — widget jest widoczny, ale nigdy nie wymaga działań. Albo przechodzi sam, albo zwraca błąd. Dobry na strony, gdzie nie można dopuścić zbędnych kliknięć.
- Invisible — widget w ogóle się nie renderuje. Weryfikacja idzie w tle. Wygodny dla przycisków i formularzy, gdzie nie chcesz zmieniać designu, ale wymaga starannej obsługi błędów.
Krok 1: Zrozum, co dokładnie Turnstile widzi po Twojej stronie
Cel etapu: zanim zaczniesz cokolwiek konfigurować, musisz rozumieć, jakie dane o Twoim środowisku otrzymuje widget. To baza do diagnostyki w kolejnych krokach i do świadomej pracy przez proxy mobilne.
Jak zobaczyć pracę widgetu na własne oczy
- Otwórz dowolną stronę, na której stoi Cloudflare Turnstile. Takie widgety łatwo rozpoznać po logo Cloudflare w prawym dolnym rogu bloku i linkach „Privacy” i „Terms”.
- Naciśnij F12 albo kliknij prawym przyciskiem myszy — „Zbadaj”, żeby otworzyć narzędzia deweloperskie.
- Przejdź na zakładkę „Network” (Sieć) i odśwież stronę.
- W polu filtra wpisz challenges.cloudflare.com. Zobaczysz kilka zapytań: ładowanie api.js, ładowanie samego iframe widgetu i jedno lub kilka zapytań POST — to właśnie wysyłka wyników sprawdzeń.
- Otwórz zakładkę „Elements” (Elementy) i znajdź blok z klasą cf-turnstile. Wewnątrz niego po udanej weryfikacji pojawi się ukryte pole input z nazwą cf-turnstile-response i długim ciągiem w wartości. To token.
Wskazówka: Zawartość zapytań POST jest zaszyfrowana i zaciemniona, czytanie jej jest bezcelowe. Patrz na coś innego: ile zapytań poszło, ile czasu zajęła weryfikacja i czy status widgetu zmienił się na „Success”. To Twój zewnętrzny wskaźnik zaufania.
Co Turnstile widzi o Twojej przeglądarce
Widget wykonuje JavaScript bezpośrednio w Twoim oknie, więc ma dostęp do wszystkiego, do czego ma dostęp każdy skrypt na stronie: wersja silnika, zainstalowane API, rozmiary ekranu, strefa czasowa, języki interfejsu, cechy renderowania grafiki i czcionek, zachowanie funkcji, które w zautomatyzowanych przeglądarkach bywają nadpisane. Nie czyta Twoich plików i nie zagląda do innych kart. Ale doskonale zauważa, gdy przeglądarka deklaruje jedno, a robi drugie. Na przykład User-Agent mówi „Chrome na Androidzie”, a w środowisku nie ma zdarzeń dotykowych i są API, których na urządzeniach mobilnych nie bywa.
Co Turnstile widzi o Twojej sieci
Tu zaczyna się najciekawsze dla tych, którzy pracują przez proxy mobilne. Wszystkie zapytania widgetu idą na serwery Cloudflare, a więc Cloudflare widzi Twój zewnętrzny adres IP, jego system autonomiczny (czyli operatora), kraj, a także niskopoziomowe cechy połączenia — jak dokładnie Twój klient ustanawia szyfrowane połączenie. Te cechy są różne w różnych przeglądarkach, a Cloudflare porównuje je z zadeklarowanym User-Agentem.
Operatorzy mobilni przydzielają adresy z dużych wspólnych pul, za jednym adresem jednocześnie siedzą setki realnych abonentów. Dlatego takie adresy same w sobie mają neutralną lub dobrą reputację — zablokować je oznaczałoby zablokować realnych ludzi. Ale reputacja to tylko jeden z sygnałów. Jeśli z adresu mobilnego przychodzi przeglądarka, której odcisk sieciowy jest odciskiem skryptu desktopowego, strefa czasowa z innego kontynentu i są oznaki automatyzacji, obraz przestaje się zgadzać i widget przełącza się w tryb interaktywny albo odmawia.
Co Turnstile widzi o Twoim zachowaniu
W trybie zarządzanym widget zwraca uwagę na to, jak szybko formularz jest wysyłany po załadowaniu, czy użytkownik wchodził w interakcję ze stroną, czy kliknięcie checkboxa wygląda naturalnie. W trybach niewidocznym i nieinteraktywnym komponent behawioralny jest minimalny — decyzja podejmowana jest po środowisku i sieci.
✅ Sprawdzenie: Na tym etapie powinieneś umieć otworzyć zakładkę Network, odfiltrować zapytania do challenges.cloudflare.com, zobaczyć ukryte pole cf-turnstile-response i wyjaśnić własnymi słowami trzy grupy sygnałów: przeglądarka, sieć, zachowanie. Jeśli się udało — przechodź do tworzenia własnego widgetu.
Możliwe problemy
- Zapytań do challenges.cloudflare.com nie ma w ogóle. Najprawdopodobniej domena jest blokowana przez rozszerzenie przeglądarki lub filtr firmowy. Wyłącz blokery na czas testów.
- Widget wisi w stanie weryfikacji bez końca. Sprawdź czas systemowy na komputerze: duże rozbieżności z rzeczywistym czasem psują weryfikację.
Krok 2: Tworzymy widget w panelu Cloudflare
Cel etapu: zdobyć parę kluczy — site key i secret key — i poprawnie skonfigurować listę domen oraz tryb działania.
- Otwórz panel zarządzania Cloudflare i zaloguj się na konto. Jeśli nie masz konta — kliknij „Sign up”, wpisz e-mail i hasło, potwierdź wiadomość.
- W lewym menu znajdź pozycję Turnstile. Jeśli masz kilka kont, najpierw wybierz odpowiednie na stronie głównej.
- Kliknij niebieski przycisk Add widget (Dodaj widget).
- W polu Widget name wpisz zrozumiałą nazwę, np. „Landing zgłoszenia — główny”. Nazwę widzisz tylko Ty, ale przy kilkunastu widgetach uratuje Cię od pomyłek.
- W bloku Hostname management kliknij Add hostnames i wpisz domeny, na których widget ma działać. Wpisuj bez protokołu i bez ścieżki: example.pl, a nie https://example.pl/form. Subdomeny trzeba dodawać osobno albo wskazać domenę główną — wtedy subdomeny też będą dozwolone.
- Do lokalnych testów dodaj do listy localhost. Jest to oficjalnie wspierane i nie przeszkadza w pracy produkcyjnej.
- W bloku Widget Mode wybierz tryb. Na pierwszy raz weź Managed — zobaczysz wtedy wszystkie stany widgetu, w tym interaktywny.
- Opcję Pre-clearance na razie zostaw wyłączoną. Jest potrzebna tylko wtedy, gdy strona jest proxy'owana przez Cloudflare, a o tym pogadamy w rozdziale zaawansowanym.
- Kliknij Create.
- Na następnym ekranie zobaczysz dwa pola: Site Key i Secret Key. Skopiuj oba do pliku notatek. Klucz sekretny można zobaczyć później w ustawieniach widgetu, ale wygodniej zapisać od razu.
Wskazówka: Utwórz od razu dwa widgety — jeden dla domeny produkcyjnej, drugi z nazwą „Test” i hostname'em localhost. Będziesz eksperymentować z trybami i ustawieniami, nie ruszając statystyk produkcyjnego widgetu.
Jak wygląda poprawny wynik
W liście Turnstile pojawi się karta z nazwą widgetu, jego trybem i listą hostname'ów. Site key zaczyna się od „0x” i ma około 24 znaków, secret key — też od „0x”, ale jest dłuższy. Jeśli klucz wygląda inaczej, najprawdopodobniej skopiowałeś nie to pole.
✅ Sprawdzenie: W Twoim pliku notatek zapisane są site key, secret key, nazwa widgetu, lista hostname'ów i wybrany tryb. W panelu Cloudflare widget widnieje na liście ze statusem aktywnego.
Możliwe problemy
- Przycisk Create jest nieaktywny. Nie dodano żadnego hostname'a albo wpisano go z błędem (protokół, ukośnik, spacja).
- Nie ma pozycji Turnstile w menu. Jesteś wewnątrz ustawień konkretnej domeny. Wróć na poziom konta — Turnstile mieszka tam, a nie w strefie.
Krok 3: Wbudowujemy widget na stronę z formularzem
Cel etapu: widget wyświetla się na Twojej stronie, przechodzi weryfikację i wstawia token do formularza.
Podłączamy skrypt
- Otwórz plik HTML strony z formularzem w edytorze.
- Wewnątrz tagu head lub przed zamykającym tagiem body dodaj wiersz podłączenia skryptu:
<script src='https://challenges.cloudflare.com/turnstile/v0/api.js' async defer></script>Atrybuty async i defer pozwalają stronie nie czekać na załadowanie skryptu. Widget pojawi się trochę później, ale użytkownik nie zobaczy opóźnienia w ładowaniu treści.
Umieszczamy kontener widgetu
- Znajdź formularz, który chronisz. Zwykle to tag form z polami imienia, maila, telefonu.
- Tuż przed przyciskiem wysyłania wstaw pusty blok z klasą cf-turnstile i Twoim site key:
<form action='/submit.php' method='POST'> <input type='text' name='name' placeholder='Twoje imię'> <input type='email' name='email' placeholder='E-mail'> <div class='cf-turnstile' data-sitekey='TWOJ_SITE_KEY' data-theme='light'></div> <button type='submit'>Wyślij</button> </form>- Zastąp TWOJ_SITE_KEY kluczem z pliku notatek. Klucza sekretnego tu wstawiać nie wolno.
- Zapisz plik i otwórz stronę w przeglądarce przez localhost.
Co powinieneś zobaczyć
W sekundę lub dwie po załadowaniu w miejscu bloku pojawi się widget o rozmiarze około 300 na 65 pikseli. Najpierw pokazuje wskaźnik ładowania i tekst „Verifying”, potem — zielony znaczek i „Success”. Jeśli Cloudflare zdecydował o ponownej weryfikacji środowiska, pojawi się checkbox z tekstem „Verify you are human” — kliknij go, a po chwili widget pokaże sukces.
Otwórz narzędzia deweloperskie, zakładkę Elements, i rozwiń blok cf-turnstile. Wewnątrz pojawiło się ukryte pole input z nazwą cf-turnstile-response. Jego wartość to długi token. Właśnie on pójdzie na serwer przy wysyłce formularza.
Przydatne atrybuty kontenera
- data-theme — light, dark lub auto. Auto dopasowuje się do motywu systemowego użytkownika.
- data-size — normal, compact lub flexible. Flexible rozciąga widget na szerokość kontenera — wygodne przy mobilnym layoucie.
- data-language — kod języka, np. pl. Domyślnie widget bierze język przeglądarki.
- data-action — krótka etykieta, np. login lub checkout. Wróci w odpowiedzi siteverify i pomoże rozróżnić formularze w statystykach.
- data-callback — nazwa funkcji JavaScript, która wywoła się po sukcesie. Dostanie w argumencie token.
- data-error-callback — funkcja, która otrzyma kod błędu, jeśli coś poszło nie tak.
- data-refresh-expired — co robić, gdy token wygasł: auto pobiera nowy sam, manual pokazuje przycisk odświeżenia, never nic nie robi.
Wskazówka: Od razu dodaj data-error-callback i wypisuj kod błędu do konsoli. Kody Turnstile są informatywne: seria 110xxx mówi o problemach z kluczem lub domeną, 300xxx — o błędzie wykonania w przeglądarce, 600xxx — o tym, że weryfikacja nie została zaliczona. Bez tego będziesz zgadywać, dlaczego widget milczy.
Alternatywna droga: jawny render przez JavaScript
Jeśli pracujesz we frameworku albo chcesz kontrolować moment pojawienia się widgetu, zastąp niejawny render jawnym. Dodaj do adresu skryptu parametr render=explicit i wywołaj turnstile.render z potrzebnymi parametrami:
turnstile.render('#my-widget', { sitekey: 'TWOJ_SITE_KEY', theme: 'auto', action: 'signup', callback: function(token) { console.log('Token otrzymany', token.length); } });Taki sposób pozwala przerysować widget po błędzie metodą turnstile.reset i pobrać aktualny token metodą turnstile.getResponse.
✅ Sprawdzenie: Widget wyświetla się na stronie, pokazuje „Success”, w DOM jest pole cf-turnstile-response z tokenem, w konsoli nie ma błędów. Spróbuj odświeżyć stronę trzy-cztery razy — za każdym razem powinien pojawić się nowy token.
Możliwe problemy
- Widget pokazuje błąd 110200. Domena, z której otwarta jest strona, nie jest dodana do hostname'ów widgetu. Sprawdź, czy otwierasz właśnie przez localhost, a nie przez 127.0.0.1 albo file:// — to różne hostname'y.
- Widget się nie pojawia, w konsoli pusto. Skrypt się nie załadował. Sprawdź adres skryptu pod kątem literówek i braku blokerów.
- Widget psuje layout. Użyj data-size='flexible' albo owiń blok w kontener o odpowiedniej szerokości.
Krok 4: Konfigurujemy serwerową weryfikację tokenu
Cel etapu: serwer odrzuca każde wysłanie formularza bez ważnego tokenu. To najważniejszy krok — bez niego widget zostaje tylko ozdobą, bo bot może wysłać zapytanie POST bezpośrednio, z pominięciem strony.
Jak zbudowane jest zapytanie do siteverify
Twój serwer wysyła zapytanie POST pod adres https://challenges.cloudflare.com/turnstile/v0/siteverify z polami:
- secret — Twój klucz sekretny;
- response — token z pola cf-turnstile-response;
- remoteip — IP odwiedzającego, opcjonalnie, ale przydatnie;
- idempotency_key — opcjonalny unikalny identyfikator zapytania, o nim w rozdziale zaawansowanym.
W odpowiedzi przychodzi JSON. Kluczowe pola:
{ "success": true, "challenge_ts": "2026-03-14T10:22:31.000Z", "hostname": "example.pl", "error-codes": [], "action": "signup", "cdata": "" }Token żyje 300 sekund i jest jednorazowy. Ponowna weryfikacja tego samego tokenu zwróci błąd timeout-or-duplicate.
Przykład w PHP
- Otwórz plik obsługujący formularz, np. submit.php.
- Na samym początku, przed jakąkolwiek pracą z danymi formularza, dodaj blok weryfikacji:
<?php $token = $_POST['cf-turnstile-response'] ?? ''; if ($token === '') { http_response_code(400); exit('Weryfikacja niezaliczona: brak tokenu'); } $data = [ 'secret' => getenv('TURNSTILE_SECRET'), 'response' => $token, 'remoteip' => $_SERVER['REMOTE_ADDR'] ]; $ch = curl_init('https://challenges.cloudflare.com/turnstile/v0/siteverify'); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data)); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_TIMEOUT, 10); $raw = curl_exec($ch); curl_close($ch); $result = json_decode($raw, true); if (empty($result['success'])) { http_response_code(403); exit('Weryfikacja niezaliczona: ' . implode(',', $result['error-codes'] ?? ['no-response'])); }