letsdeploy / statamic-turnstile
Requires
- statamic/cms: ^6.0
Requires (Dev)
- orchestra/testbench: ^10.8
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
Protect Statamic forms with Cloudflare Turnstile. Works with full static caching.
Features
- A Turnstile field you can add to any form blueprint.
- Every submission of a form with that field is checked with Cloudflare. Forms without it are left alone.
- A friendly error when the check fails, in English and Dutch.
- Cloudflare's script only loads once a widget scrolls into view.
- Works inside
{{ nocache }}, so forms keep working with thefullstatic caching strategy.
Installation
composer require letsdeploy/statamic-turnstile
Add your keys to .env:
TURNSTILE_SITE_KEY=your-site-key TURNSTILE_SECRET_KEY=your-secret-key
For local development you can use Cloudflare's
test keys,
for example 1x00000000000000000000AA / 1x0000000000000000000000000000000AA (always passes).
Usage
-
Add the Turnstile field to your form blueprint.
-
Put the loader in your layout's
<head>, once, outside any{{ nocache }}tag:<head> ... {{ turnstile:scripts }} </head>
-
Render the form fields as usual. If you write the HTML yourself, render just the Turnstile field:
{{ form:contact }} ... {{ fields }} {{ if type == 'turnstile' }}{{ field }}{{ /if }} {{ /fields }} {{ /form:contact }}
How it works with static caching
With the full strategy, Statamic serves a plain HTML file and fills in
{{ nocache }} regions with JavaScript after the page loads. Scripts inside
those regions never run, and Turnstile's automatic rendering only scans the
page once, so it would miss the form.
That's why this addon renders widgets explicitly:
- The field outputs an empty
<div data-turnstile>box. {{ turnstile:scripts }}waits for the DOM to be ready, looks for those boxes, and looks again when Statamic firesstatamic:nocache.replaced.- When a box scrolls into view, it loads Cloudflare's script
(
?render=explicit) and renders the widget in it.
The widget posts its token under the field's handle, so the field's own validation rule sends it to Cloudflare's Siteverify API. The token is never stored with the submission.
Translations
The error message lives in lang/{en,nl}/messages.php. To change it, publish the translations:
php artisan vendor:publish --tag=statamic-turnstile-translations
Testing
composer install vendor/bin/phpunit