Skip to content

Commit d5f422b

Browse files
[Turnstile] Tutorial for conditionally enforcing Turnstile with HTMLRewriter (#21311)
* [Turnstile] Tutorial for conditionally enforcing Turnstile with HTMLRewriter * Apply suggestions from code review Co-authored-by: Patricia Santa Ana <[email protected]> * demo * Update src/content/docs/turnstile/tutorials/conditionally-enforcing-turnstile.mdx --------- Co-authored-by: Patricia Santa Ana <[email protected]>
1 parent 01ade92 commit d5f422b

File tree

1 file changed

+108
-0
lines changed

1 file changed

+108
-0
lines changed
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
---
2+
title: Conditionally enforce Turnstile
3+
pcx_content_type: tutorial
4+
updated: 2025-04-01
5+
difficulty: Intermediate
6+
languages:
7+
- TypeScript
8+
tags:
9+
- Node.js
10+
sidebar:
11+
order: 6
12+
---
13+
14+
This tutorial explains how to conditionally enforce Turnstile based on the incoming request, such as a pre-shared secret in a header or a specific IP address.
15+
16+
## Overview
17+
18+
You may have setups such as automation that cannot load or run the Turnstile challenge. Using [`HTMLRewriter`](/workers/runtime-apis/html-rewriter/), this tutorial will demonstrate how to conditionally handle the [client-side widget](/turnstile/get-started/client-side-rendering/) and [siteverify API](/turnstile/get-started/server-side-validation/) when specific criteria are met.
19+
20+
:::note
21+
While this tutorial removes Turnstile client-side elements when specific criteria are met, you could instead conditionally insert them.
22+
:::
23+
24+
:::caution
25+
It is critical to make sure you are validating tokens with the siteverify API when your criteria for enforcing Turnstile are not met.
26+
27+
It is not sufficient to only remove the client-side widget from the page, as an attacker can forge the request to your API.
28+
:::
29+
30+
## Implementation
31+
32+
This tutorial will modify the existing [Turnstile demo](https://github.com/cloudflare/turnstile-demo-workers/blob/main/src/) to conditionally remove the existing `script` and widget container elements.
33+
34+
```diff title="src/index.mjs" lang="js"
35+
export default {
36+
async fetch(request) {
37+
// ...
38+
39+
+ if (request.headers.get("x-bypass-turnstile") === "VerySecretValue") {
40+
+ class RemoveHandler {
41+
+ element(element) {
42+
+ element.remove();
43+
+ }
44+
+ }
45+
+
46+
+ return new HTMLRewriter()
47+
+ // Remove the script tag
48+
+ .on(
49+
+ 'script[src="https://challenges.cloudflare.com/turnstile/v0/api.js"]',
50+
+ new RemoveHandler(),
51+
+ )
52+
+ // Remove the container used in implicit rendering
53+
+ .on(
54+
+ '.cf-turnstile',
55+
+ new RemoveHandler(),
56+
+ )
57+
+ // Remove the container used in explicit rendering
58+
+ .on(
59+
+ '#myWidget',
60+
+ new RemoveHandler(),
61+
+ )
62+
+ .transform(body);
63+
+ }
64+
65+
return new Response(body, {
66+
headers: {
67+
"Content-Type": "text/html",
68+
},
69+
});
70+
},
71+
};
72+
```
73+
74+
## Server-side integration
75+
76+
We will exit early in our validation if the same logic we used to remove the client-side elements is present.
77+
78+
:::caution
79+
The same logic must be used in both the client-side and the server-side implementations.
80+
:::
81+
82+
```diff lang="js" title="src/index.mjs"
83+
async function handlePost(request) {
84+
+ if (request.headers.get("x-bypass-turnstile") === "VerySecretValue") {
85+
+ return new Response('Turnstile not enforced on this request')
86+
+ }
87+
// Proceed with validation as normal!
88+
const body = await request.formData();
89+
// Turnstile injects a token in "cf-turnstile-response".
90+
const token = body.get('cf-turnstile-response');
91+
const ip = request.headers.get('CF-Connecting-IP');
92+
// ...
93+
}
94+
```
95+
96+
With these changes, Turnstile will not be enforced on requests with the header `x-bypass-turnstile: VerySecretValue` present.
97+
98+
## Demonstration
99+
100+
After running `npm run dev` in the project folder, you can test the changes by running the following command:
101+
102+
```sh
103+
curl -X POST http://localhost:8787/handler -H "x-bypass-turnstile: VerySecretValue"
104+
```
105+
106+
```txt output
107+
Turnstile not enforced on this request
108+
```

0 commit comments

Comments
 (0)