A collection of practical examples for integrating Cloudflare Turnstile into web projects. Each example shows a real pattern for reading the widget, handling the challenge, and exchanging a token with your backend.
- A minimal HTML widget setup (managed and non-interactive modes)
- Reading the
sitekeyand widget state from the page - Server-side token verification flow
- Automated-browser examples (Playwright, Selenium, Puppeteer) that let the widget run naturally
- API-based token injection when you need it headless
- Prerequisites
- 1. Basic widget setup
- 2. Reading the sitekey
- 3. Server-side verification
- 4. Automated browser flow
- 5. API token injection
- License
- A Cloudflare Turnstile sitekey (from your Cloudflare dashboard)
- A normal web server or framework for the examples
- Optional: a Peak API key for the API-based token example
Turnstile embeds through a single div. In managed mode Cloudflare decides whether to show an interactive challenge:
<div class="cf-turnstile" data-sitekey="0x4AAAAAAAxxxx" data-theme="light"></div>
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>In non-interactive mode the widget runs invisibly and real users never see it:
<div class="cf-turnstile" data-sitekey="0x4AAAAAAAxxxx" data-mode="non-interactive"></div>The widget sets a hidden cf-turnstile-response field that your form submits
with the rest of the data.
Sometimes you need the sitekey at runtime rather than hard-coding it. Reading it from the DOM is a stable approach:
const el = document.querySelector(".cf-turnstile");
const sitekey = el && el.getAttribute("data-sitekey");
console.log("sitekey:", sitekey);The same attribute is present in server-rendered markup, so a scraper can also extract it without executing the widget.
A token from the widget must be verified with Cloudflare's siteverify endpoint before you trust it. A minimal example in Node.js:
const res = await fetch(
"https://challenges.cloudflare.com/turnstile/v0/siteverify",
{
method: "POST",
headers: { "content-type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
secret: process.env.TURNSTILE_SECRET_KEY,
response: req.body["cf-turnstile-response"],
}),
}
);
const data = await res.json();
console.log("success:", data.success);If you automate a browser, let the widget run in a real page and wait for the challenge to finish, then capture the token. Example with Puppeteer:
const page = await browser.newPage();
await page.goto("https://example.com/protected");
await page.waitForSelector(".cf-turnstile iframe", { timeout: 15000 });
const token = await page.evaluate(() => {
const el = document.querySelector(".cf-turnstile");
return el && el._callback ? el._callback("") : null;
});
console.log("token:", token);When you need a token without driving a full browser, an API can produce one from the sitekey and page URL. The example below uses Peak:
POST https://api.peak.fo/solve
{
"task_type": "turnstiletask",
"sitekey": "0x4AAAAAAAxxxx",
"url": "https://example.com/protected"
}Then submit the returned token as cf-turnstile-response:
cf-turnstile-response: 0.abc123...
This collection uses Peak to solve Turnstile.
- Solve Cloudflare Turnstile & the 5s challenge in about a second
- Pay only for successful solves - from $1 / 1,000
- Free API key, no card. Use code
PEAKGHfor bonus trial credit.
Get your free API key • Docs • Pricing
MIT