Cloudflare Turnstile CAPTCHA Checkbox with PHP

Cloudflare's Turnstile is a modern alternative to other traditional CAPTCHA systems and providers. It helps to balance website's security without disturbing the user experience. Learn how to implement Cloudflare Turnstile checkbox CAPTCHA in PHP with minimal effort.

Cloudflare Turnstile CAPTCHA Checkbox

Cloudflare Turnstile is a new human verification tool which serves as an alternative to other CAPTCHA systems. It aims to provide bot protection without complex challenges like other traditional CAPTCHA systems. This post demonstrates how to add Cloudflare Turnstile CAPTCHA in PHP in simple and easy way. To implement Cloudflare Turnstile we are going to create these files:

  1. index.php: Contains the HTML content with Cloudflare checkbox.
  2. verify-turnstile.php: The server-side script to verify Turnstile response.
  3. javascript.js: A javascript file to prevent form submission if turnstile response is not ready.
  4. style.css: CSS Stylesheet for page. 
Cloudflare's Turnstile CAPTCHA is easy to implement and requires less effort. It does not require user tracking with cookies. It provides better user experience unlike other CAPTCHA services. In short it provides bots protection without privacy concerns with better user experience.

Create a Turnstile Widget in Cloudflare

In order to implement cloudflare CAPTCHA we need to create a Turnstile widget in cloudflare account. Follow the steps below to create a Turnstile Widget:

  • Login to your cloudflare account dashboard.
  • In the left side navigation menu click "Turnstile".
  • Click on "+ Add Widget" button on Turnstile widget page.
  • Enter a widget name of your choice.
  • Click on "+ Add Hostnames".
  • Add the hostnames you want to allow to use this widget.
  • In "Widget Mode" section keep the "Managed" option checked.
  • Click on "Create". 
Create a Turnstile Widget in Cloudflare

 

Get Turnstile Site Key and Secret Key

After the widget has been created following the above steps. A page with Site Key and Secret Key will be displayed. Copy both and store them in a constant file.

Turnstile Site Key & Site Widget

 

Create Turnstile Constants File

Create a PHP file constants.php to store Turnstile challenges URL and site key and secret key.

constants.php

<?php
define('TURNSTILE_CHALLENGES_URL', 'https://challenges.cloudflare.com/turnstile/v0');
define('TURNSTILE_SITE_KEY', 'YOUR_TURNSTILE_SITE_KEY');
define('TURNSTILE_SECRET_KEY', 'YOUR_TURNSTILE_SECRET_KEY');

Add Cloudflare Turnstile Checkbox CAPTCHA to HTML Form

Create a web page with HTML content and form, load the turnstile challenges script and add turnstile CAPTCHA container.

index.php

<?php include_once 'constants.php'?>
<!DOCTYPE html>
<html>
<head>
<title>Cloudflare Turnstile CAPTCHA Checkbox with PHP - Demo</title>
<meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
<meta content="width=device-width, initial-scale=1, maximum-scale=1" name="viewport" />
<script src="<?=TURNSTILE_CHALLENGES_URL;?>/api.js" defer></script>
<script type="text/javascript" src="js/javascript.js"></script>
<link rel="stylesheet" href="css/style.css" />
</head>
<body>
<section class="section py-4">
<div class="container">
<form action="verify-turnstile.php" method="POST" class="turnstile-form mb-4">
<div class="mb-4">
<div class="cf-turnstile"
data-sitekey="<?=TURNSTILE_SITE_KEY;?>"
data-size="flexible"
data-response-field-name="cf_turnstile_response"></div>
</div>
<div class="mb-4">
<button type="submit" class="btn btn-blue">Send</button>
</div>
</form>
</div>
</section>
</body>
</html>

Prevent Form Submissions without Human Verification

Add javascript to prevent form submission and show an alert if user has not verified the Turnstile checkbox.

javascript.js

document.addEventListener("DOMContentLoaded", function(){
document.querySelector(".turnstile-form")
.addEventListener("submit", function(e) {
let turnstile_response = document.querySelector('input[name="cf_turnstile_response"]');
if (!turnstile_response || !turnstile_response.value) {
e.preventDefault(); // Stop form submission
alert("Cloudflare CAPTCHA is required.");
}
});
});
 

Verify Turnstile Response on Server-Side

Create PHP file to handle form submission and verify turnstile response submitted with form. Steps to verify turnstile CAPTCHA response are:

  • Include constants file to use them in script.
  • Sanitize the input $_POST array.
  • Create a new cURL instance.
  • Add the challenges API endpoint as cURL option.
  • Pass the Secret Key as "secret" parameter in data of cURL request and turnstile "response" submitted with form for verification.
  • Execute cURL request and close the cURL session.
  • Decode the API response with json_decode() function.
  • Check if response was not success then redirect user back to same page. You can add an error message in session here.
  • If response was successful, Process the form. 

verify-turnstile.php

<?php
include_once 'constants.php';

$post = filter_input_array(INPUT_POST, [
'cf_turnstile_response' => FILTER_SANITIZE_SPECIAL_CHARS
]);

$curl_handle = curl_init();

curl_setopt($curl_handle, CURLOPT_URL, TURNSTILE_CHALLENGES_URL . '/siteverify');
curl_setopt($curl_handle, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl_handle, CURLOPT_POST, true);
curl_setopt($curl_handle, CURLOPT_POSTFIELDS, http_build_query([
'secret' => TURNSTILE_SECRET_KEY,
'response' => $post['cf_turnstile_response'],
]));

$response = curl_exec($curl_handle);

curl_close($curl_handle);

// Decode the JSON response
$response_data = json_decode($response, true);

// If Turnstile response was not successful, redirect user with error message here
if(!$response_data['success']){
header('Location: ' . filter_var($_SERVER['HTTP_REFERER'], FILTER_SANITIZE_URL));
}

// Everything worked fine, Process your form here

Add CSS Styles

Add CSS styles for entire web page and turnstile container.

style.css

* {
box-sizing: border-box;
}
html,body {
margin: 0;
padding: 0;
}
body {
background-color: #f6f6f6;
font-family: "Segoe UI", "Roboto", "Helvetica", sans-serif;
font-size: 15px;
font-weight: normal;
font-style: normal;
}
.container {
max-width: 1024px;
margin: 0 auto;
padding-left: 15px;
padding-right: 15px;
}
.py-4 {
padding-top: 1rem;
padding-bottom: 1rem;
}
.my-1, .mb-1 {
margin-bottom: .25rem;
}
.my-4, .mb-4 {
margin-bottom: 1rem;
}
.btn {
display: inline-block;
padding: .375rem .75rem;
cursor: pointer;
font-size: inherit;
}
.btn-blue {
background-color: #0369a1;
border: 1px solid #0369a1;
color: #ffffff;
}
.btn-blue:hover {
background-color: #005D95;
}
.cf-turnstile{
min-height: 65px;
}

With these code snippets we implemented Cloudflare's Turnstile Captcha Checkbox for bots protection. Do have a look at Turnstile product page for pricing and features it provides.