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 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:
- index.php: Contains the HTML content with Cloudflare checkbox.
- verify-turnstile.php: The server-side script to verify Turnstile response.
- javascript.js: A javascript file to prevent form submission if turnstile response is not ready.
- style.css: CSS Stylesheet for page.
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".

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.

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.