You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
- The Turnstile script is not loaded until a challenge-required response is received.
147
-
- The rendered widget uses invisible mode.
152
+
- The Turnstile widget should be configured in Cloudflare as **Invisible** mode. Client code uses explicit rendering with `execution: "execute"`; `size: "invisible"` is not a valid current Turnstile size option.
148
153
- The widget is removed after callback success or failure.
149
154
- Siteverify is called only when a challenge-required request retries with a token.
150
155
- A valid challenge creates a signed, HttpOnly, Secure, SameSite=Lax clearance cookie scoped to `/examples`.
@@ -176,47 +181,100 @@ This is only an app-level bypass. If Cloudflare Rate Limiting challenges smoke t
The best first production protection for Dynamic Worker cost is Cloudflare Rate Limiting, because it runs before the Worker and directly targets repeated POST runs.
184
+
The best first production protection for Dynamic Worker cost is Cloudflare Rate Limiting, because it runs before the Worker and limits repeated runner traffic at the edge.
Important Cloudflare details that affect this project:
194
+
195
+
- Rate limit counters are kept per unique combination of configured characteristics, and Cloudflare implicitly includes the data-center ID (`cf.colo.id`). They are not global counters across the whole Cloudflare network.
196
+
- Available expression fields, characteristics, counting periods, mitigation periods, and rule counts vary by Cloudflare plan.
197
+
- Per Cloudflare's availability table, matching on HTTP method in a rate limiting rule expression is available on Business and Enterprise plans, not Free/Pro.
198
+
- On Free/Pro/Business, challenge actions (`managed_challenge`, `js_challenge`, `challenge`) use request throttling; you do not configure a mitigation duration. After a visitor passes the challenge, that request counter is reset. Enterprise can configure challenge-action duration.
199
+
- Cloudflare Challenge Pages interrupt the request flow and are not ideal for AJAX/fetch API-style calls. The runner POST is submitted by browser JavaScript, so a `Block` action with a `429` response is more predictable for high-rate POST abuse than a Managed Challenge on the POST itself. Use app-level Turnstile, or Cloudflare Turnstile Pre-clearance, when the desired behavior is browser verification rather than a hard edge cap.
200
+
201
+
### Business/Enterprise precise POST rule
202
+
203
+
If the zone plan supports the `http.request.method` field in rate limiting expressions, use this precise rule:
184
204
185
205
```text
186
206
Rule name:
187
-
Protect Python By Example runner
207
+
Protect Python By Example runner POSTs
188
208
189
-
Expression:
209
+
When incoming requests match:
190
210
http.request.method eq "POST"
191
211
and starts_with(http.request.uri.path, "/examples/")
192
212
193
-
Threshold:
194
-
30 requests / 60 seconds / IP
213
+
With the same characteristics:
214
+
IP
215
+
# or IP with NAT support, if available and classroom/shared-network false positives matter
195
216
196
-
Action:
197
-
Managed Challenge
217
+
When rate exceeds:
218
+
30 requests / 60 seconds
198
219
199
-
Mitigation timeout:
220
+
Then take action:
221
+
Block
222
+
223
+
Response code:
224
+
429
225
+
226
+
Duration:
200
227
5 minutes
201
228
```
202
229
203
230
For classrooms/workshops behind one NAT, start higher:
204
231
205
232
```text
206
-
100 requests / 60 seconds / IP
233
+
100 requests / 60 seconds / IP or IP-with-NAT-support bucket
234
+
```
235
+
236
+
### Free/Pro fallback rule
237
+
238
+
If the zone plan does not allow `http.request.method` in rate limiting expressions, do **not** use the POST-only expression above. Use a path-only rule and set the threshold high enough that normal page reading is unaffected:
WAF rules are a good second layer for risky-looking traffic, for example low reputation, suspicious user agents, or bot-management signals if available on the Cloudflare plan.
229
287
230
-
Shape:
288
+
For top-level HTML page requests, a Managed Challenge custom rule can be appropriate:
231
289
232
290
```text
233
-
http.request.method eq "POST"
234
-
and starts_with(http.request.uri.path, "/examples/")
For the AJAX/fetch runner endpoint itself, avoid returning a Challenge Page directly to the POST request. Cloudflare documents that Challenge Pages can fail when the browser expects a non-HTML AJAX/XHR/fetch response. Prefer one of these instead:
304
+
305
+
- app-level Turnstile, as implemented here
306
+
- Cloudflare Turnstile Pre-clearance if integrating WAF challenges with API requests
307
+
-`Block` / `429` for hard abuse caps
245
308
246
309
Use WAF custom rules for risk filtering. Use Rate Limiting for volume control.
Invisible mode note: Cloudflare documents that invisible widgets have no visual footprint, and that widget `size` values are `normal`, `flexible`, or `compact`; invisibility is selected by widget mode, not by `size="invisible"`.
371
+
372
+
Privacy note: Cloudflare documents that using Invisible mode requires referencing the Cloudflare Turnstile Privacy Addendum in your own privacy policy.
302
373
303
374
Copy:
304
375
@@ -341,25 +412,24 @@ Do **not** put secret keys in `wrangler.jsonc`.
30 requests / 60 seconds / IP → Managed Challenge for 5 minutes
360
-
```
426
+
Use the plan-aware guidance above:
427
+
428
+
- Business/Enterprise: match `POST` + `/examples/`, start at `30 requests / 60 seconds`, action `Block`, response `429`, duration `5 minutes`.
429
+
- Classroom/shared NAT: start around `100 requests / 60 seconds` or use `IP with NAT support` if available.
430
+
- Free/Pro: if method matching is unavailable, use a path-only `/examples/` rule with a higher threshold because it also counts GET page views.
361
431
362
-
Raise the threshold if teaching cohorts behind a shared IP hit it.
432
+
Do not configure `Managed Challenge for 5 minutes` on Free/Pro/Business rate limiting rules. Cloudflare documents that challenge actions on those plans use request throttling and have no selected duration; only Enterprise can configure a challenge-action duration. For this AJAX/fetch POST endpoint, a predictable `Block`/`429` edge cap is preferable.
363
433
364
434
### 4. Deploy
365
435
@@ -423,7 +493,7 @@ Browser behavior:
423
493
424
494
1. click Run
425
495
2. challenge is requested
426
-
3.invisible Turnstile runs or displays only if Cloudflare needs interaction
496
+
3.Invisible-mode Turnstile runs without page-load widget furniture
427
497
4. widget disappears after callback
428
498
5. run retries and produces output
429
499
6. later runs in the same clearance window skip Turnstile
0 commit comments