Cloudflare Docs
Turnstile
Edit this page on GitHub
Set theme to dark (⇧+D)

Price scraping

You can use the templates below for a demo application in Workers where product details are shown, but pricing information is not revealed until the Turnstile widget is solved.

Worker scripts

The script below is applied on the root path <YOUR-HOSTNAME-HERE>/ }} and serves a page where the Turnstile widget will be embedded.

 Run Worker
const someHTML = `
<head>
<title>Turnstile Price Scraping Demo</title>
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js?onload=onloadTurnstileCallback" async defer></script>
<script src="<YOUR-HOSTNAME-HERE>/pricing.js"></script>
</head>
<body>
<h3>Pricing:</h3>
<div id="price-box"></div>
<div class="cf-turnstile" id="price-widget"></div>
</body>
`
export default {
async fetch(request, env, ctx) {
let returnHTMLResponse = new Response(someHTML);
returnHTMLResponse.headers.set('content-type','text/html')
return returnHTMLResponse
},
};

The script below is applied on the route <YOUR-HOSTNAME-HERE>/pricing.js and returns a client script which embeds the Turnstile widget.

 Run Worker
const clientScript = `
async function getPrice(token){
// the endpoint for retrieving pricing information
const priceInfoURL = "/get-price"
// the element id we are appending our price information
const priceBox = document.getElementById('price-box');
// await response from pricing endpoint protected by Turnstile Server-Side Validation
const response = await fetch(
priceInfoURL, {
headers : {
'cf-token' : token
}
}
);
// await price information from pricing endpoint
const price = await response.json();
// set the pricing information to what’s returned in the callback
priceBox.innerText = price.pricing
}
function init(){
window.onloadTurnstileCallback = function () {
turnstile.render('#price-widget', {
sitekey: '<YOUR-SITEKEY-GOES-HERE>',
callback: function(token) {
getPrice(token)
},
});
};
}
window.addEventListener ?
window.addEventListener("load", init, false) :
window.attachEvent && window.attachEvent("onload", init)
`
export default {
async fetch(request, env, ctx) {
if(clientScript == null || undefined){
let response = new Response("// sorry no script available!")
const { headers } = response;
headers.append('content-type', 'application/javascript');
return response;
} else {
let response = new Response(clientScript);
const { headers } = response;
headers.append('content-type', 'application/javascript');
return response;
}
}
};

The script below is applied on the route <YOUR-HOSTNAME-HERE>/get-price, and verifies the Turnstile token and returns an API or JSON pricing response.

 Run Worker
// This is the demo secret key. In production, we recommend
// you store your secret key(s) safely.
const SECRET_KEY = '<SECRET-KEY>';
async function handlePost(request) {
const token = request.headers.get('cf-token');
const ip = request.headers.get('CF-Connecting-IP');
// Validate the token by calling the
// "/siteverify" API endpoint.
let formData = new FormData();
formData.append('secret', SECRET_KEY);
formData.append('response', token);
formData.append('remoteip', ip);
const url = 'https://challenges.cloudflare.com/turnstile/v0/siteverify';
const result = await fetch(url, {
body: formData,
method: 'POST'
});
const outcome = await result.json();
// this is the conditional block that you can customize to fit your specific use-case
if (outcome.success) {
// successful token validation logic
// this example uses a static variable, but using KV you could reference a catalog listing
let response = new Response('{"pricing":"$99.99"}');
response.headers.append("content-type", "application/json");
return response;
} else {
// unsuccessful token validation logic
let response = new Response('{"pricing":"$XX.XX"}')
response.headers.append("content-type", "application/json");
return response;
}
}
export default {
async fetch(request, env, ctx) {
try {
return await handlePost(request)
} catch(error){
return new Response(`{"err":"${error}"}`);
}
},
};