Search by

letsdeploy / statamic-turnstile

sembogaarts

There is no license information available for the latest version (1.0.1) of this package.

Package info

github.com/letsdeploy-nl/statamic-turnstile

pkg:composer/letsdeploy/statamic-turnstile

Statistics

Installs: 25

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

1.0.1 2026-11-03 18:26 UTC

This package is auto-updated.

Last update: 2026-11-03 18:26:39 UTC


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 the full static 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

  1. Add the Turnstile field to your form blueprint.

  2. Put the loader in your layout's <head>, once, outside any {{ nocache }} tag:

    <head>
        ...
        {{ turnstile:scripts }}
    </head>
  3. 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:

  1. The field outputs an empty <div data-turnstile> box.
  2. {{ turnstile:scripts }} waits for the DOM to be ready, looks for those boxes, and looks again when Statamic fires statamic:nocache.replaced.
  3. 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