Skip to content

Implement Turnstile using implicit and explicit rendering

Last reviewed: about 2 months ago

This tutorial will explore the two primary methods of implementing Turnstile to your website via implicit or explicit rendering using a detailed explanation, a step by step implementation guide, and examples on how to help you protect your web application security while maintaining a good user experience.

Before you begin

  • You must have a Cloudflare account.
  • You must have a sitekey and secret key. You can obtain the sitekey and secret key from the Cloudflare dashboard after adding your zone to Turnstile.
  • You must have basic knowledge of HTML and JavaScript. Familiarity with web development will help you follow along the tutorial.

Implicit rendering

Implicit rendering automatically displays the Turnstile widget on your webpage without additional JavaScript code. This set up is ideal for static pages where you want the widget to load immediately when the page loads.

To set up implicit rendering:

  1. Include the Turnstile Script: Add the Turnstile JavaScript API to your HTML file within the <head> section or just before the closing </body> tag.
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
  1. Add the Turnstile widget to your HTML: Insert a div element where you want the widget to appear.
<div class="cf-turnstile" data-sitekey="yourSitekey" data-callback="javascriptCallback"></div>

Once a challenge has been solved, a token is passed to the success callback. This token must be validated against our siteverify endpoint.

  1. Create Your Form: Turnstile is often used to protect forms on websites such as login forms or contact forms. You can embed the widget within your <form> tag.
<form action="/submit" method="POST">
<label for="name">Name:</label><br>
<input type="text" id="name" name="name" required><br>
<label for="email">Email:</label><br>
<input type="email" id="email" name="email" required><br>
<!-- Turnstile Widget -->
<div class="cf-turnstile" data-sitekey="your-site-key"></div>
<br>
<button type="submit">Submit</button>
</form>

An invisible input with the name cf-turnstile-response is added and will be sent to the server with the other fields.

Complete HTML example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Implicit Rendering with Cloudflare Turnstile</title>
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
</head>
<body>
<h1>Contact Us</h1>
<form action="/submit" method="POST">
<label for="name">Name:</label><br>
<input type="text" id="name" name="name" required><br>
<label for="email">Email:</label><br>
<input type="email" id="email" name="email" required><br>
<!-- Turnstile Widget -->
<div class="cf-turnstile" data-sitekey="your-site-key"></div>
<br>
<button type="submit">Submit</button>
</form>
</body>
</html>

Explicit rendering

Explicit rendering helps you manually control when and where the Turnstile widget appears using JavaScript. This method is suitable for dynamic content, single-page applications(SPAs), or conditional rendering based on user interactions.

To set up explicit rendering:

  1. Include the Turnstile Script with onload callback: From the previous example, modify the script tag to include an onload parameter.
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js?onload=onloadTurnstileCallback" defer></script>
  1. Create a container element: Add a <div> with an ID or class where you want the widget to appear.
<div id="my-turnstile"></div>
  1. Define the callback function: In your code, define the onloadTurnstileCallback function.
function onloadTurnstileCallback() {
turnstile.render('#my-turnstile', {
sitekey: '<YOUR_SITE_KEY>',
callback: function(token) {
console.log(`Challenge Success ${token}`);
},
});
}
Complete HTML and JavaScript example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Explicit Rendering with Cloudflare Turnstile</title>
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js?onload=onloadTurnstileCallback" defer></script>
</head>
<body>
<h1>Register Account</h1>
<form action="/register" method="POST" id="registration-form">
<label for="username">Username:</label><br>
<input type="text" id="username" name="username" required><br>
<!-- Other form fields -->
<!-- Turnstile Container -->
<div id="my-turnstile"></div>
<br>
<button type="submit">Register</button>
</form>
<script>
function onloadTurnstileCallback() {
turnstile.render('#my-turnstile', {
sitekey: '<YOUR_SITE_KEY>',
callback: function(token) {
console.log(`Challenge Success ${token}`);
},
});
</script>
</body>
</html>

In the code above, The Turnstile script loads and calls onTurnstileLoad upon completion. turnstile.render() is used to manually render the widget in the specified container. The callback function handles the response token, which you can use for server-side verification.

If the invocation is successful, the function returns a widgetId (string). If the invocation is unsuccessful, the function returns undefined.

After rendering the Turnstile widget explicitly, you may need to interact with it based on your application’s requirements. Refer to the sections below to manage the widget’s state.

Reset a widget

To reset the widget if the given widget timed out or expired, you can use the function:

turnstile.reset(widgetId);

Get the response token

Retrieve the current response token at any time:

const responseToken = turnstile.getResponse(widgetId);

Remove a widget

When a widget is no longer needed, it can be removed from the page using:

turnstile.remove(widgetId)

This will not call any callback and will remove all related DOM elements.

Server-side validation

Regardless of the rendering method, Turnstile tokens need to be verified using siteverify because the front-end widget creates a cryptographically secured token. To ensure that a token is not forged by an attacker or has not been consumed yet, it is necessary to check the validity of the token using Cloudflare’s siteverify API.

The siteverify API will only validate a token once. If a token has already been checked, the siteverify API will yield an error on subsequent verification attempts indicating that a token has already been consumed.

// This is the demo secret key. In prod, we recommend you store
// your secret key(s) safely.
const SECRET_KEY = '1x0000000000000000000000000000000AA';
async function handlePost(request) {
const body = await request.formData();
// Turnstile injects a token in "cf-turnstile-response".
const token = body.get('cf-turnstile-response');
const ip = request.headers.get('CF-Connecting-IP');
// Validate the token by calling the "/siteverify" API.
let formData = new FormData();
formData.append('secret', SECRET_KEY);
formData.append('response', token);
formData.append('remoteip', ip);
const result = await fetch('https://challenges.cloudflare.com/turnstile/v0/siteverify', {
body: formData,
method: 'POST',
});
const outcome = await result.json();
if (!outcome.success) {
return new Response('The provided Turnstile token was not valid! \n' + JSON.stringify(outcome));
}
// The Turnstile token was successfuly validated. Proceed with your application logic.
// Validate login, redirect user, etc.
// For this demo, we just echo the "/siteverify" response:
return new Response('Turnstile token successfuly validated. \n' + JSON.stringify(outcome));
}
export default {
async fetch(request) {
if (request.method === 'POST') {
return await handlePost(request);
}
const url = new URL(request.url);
let body = implicitRenderHtml;
if (url.pathname === '/explicit') {
body = explicitRenderHtml;
}
return new Response(body, {
headers: {
'Content-Type': 'text/html',
},
});
},
};

Choose between implicit and explicit rendering

Choosing the appropriate rendering method for Turnstile is important and effectively helps you to add it to your website while ensuring optimal performance and user experience. Both implicit and explicit rendering have their own advantages and are suited to different types of web applications. Refer to the table below that highlights the ideal use case for each of these rendering methods:

FeatureImplicit renderingExplicit rendering
Ease of setupSimple, minimal codeRequires additional JavaScript
Control over timingRenders automatically on page loadFull control over rendering timing
Use casesStatic contentDynamic or interactive content
CustomizationLimited to HTML attributesExtensive via JavaScript API

Best practices

  1. Always verify the Turnstile response token on your server.
  2. Ensure your implementation is accessible to all users.
  3. Test your integration across different browsers and devices.
  4. Load the Turnstile API script asynchronously to prevent blocking.

Implementing Turnstile into your website helps improve the security of your website without compromising user experience. By understanding and implementing implicit or explicit rendering, you can choose the method that best fits your website’s architecture and user interaction patterns.

  • Implicit rendering is ideal for static pages with minimal JavaScript.
  • Explicit rendering offers greater control for dynamic and interactive applications.

Remember to perform server-side validation of the response token to complete the authentication process securely.

Additional resources

  • Server-side validation: A guide on how to implement server-side validation to ensure that only valid, human-generated responses are accepted by your application.
  • Turnstile Analytics: A guide on how to access and interpret Turnstile analytics data, allowing you to monitor key metrics, access the number of challenges issued, and evaluate the challenge solve rate (CSR).
  • Turnstile API Reference: Comprehensive documentation for the Turnstile API, providing detailed information on API operations for managing Turnstile widgets, including how to list, create, and update widgets via API calls.