vuillaume-agency/symfony-turnstile

Symfony bundle for Cloudflare Turnstile - a privacy-friendly, GDPR-compliant CAPTCHA alternative. Drop-in reCAPTCHA replacement with zero user friction.

Maintainers

Package info

github.com/vuillaume-agency/symfony-turnstile

pkg:composer/vuillaume-agency/symfony-turnstile

Statistics

Installs: 1 125

Dependents: 0

Suggesters: 0

Stars: 4

Open Issues: 0

v1.1.1 2026-01-19 18:07 UTC

This package is auto-updated.

Last update: 2026-03-19 18:45:22 UTC


README

CI Minimum PHP Version Minimum Symfony Version License

A Symfony bundle to integrate Cloudflare Turnstile on your forms. Turnstile is a privacy-preserving alternative to CAPTCHA that doesn't require user interaction.

Features

  • Zero user friction — No puzzles, no clicking on traffic lights
  • GDPR-friendly — No cookie consent required, privacy-first design
  • 22 languages included — All major European languages supported
  • Symfony 8 ready — Full support for Symfony 7.4 LTS and 8.x
  • Customizable messages — Override error messages via form options or translations
  • Easy theming — Light, dark, or auto theme support
  • Fully tested — PHPUnit tests and PHPStan static analysis

Quick Start

composer require vuillaume-agency/symfony-turnstile
# .env
TURNSTILE_KEY="your-site-key"
TURNSTILE_SECRET="your-secret-key"
// In your form
->add('captcha', TurnstileType::class)

Get your free keys at Cloudflare Dashboard.

Why Turnstile over reCAPTCHA?

Feature Turnstile reCAPTCHA
Usually invisible (no puzzles) Yes Yes (v3 only)
GDPR compliant by design Yes No (requires consent)
Uses cookies No Yes
Data used for advertising No Unclear
Script size ~30KB ~80KB
Free unlimited Yes Yes (with limits)

GDPR considerations

For European websites, reCAPTCHA poses significant compliance challenges:

  • Cookies: reCAPTCHA sets cookies that require user consent under GDPR
  • Data transfer: User data is sent to Google servers in the US
  • Consent required: Must obtain explicit consent before loading reCAPTCHA
  • Fines: French DPA (CNIL) has fined companies for improper reCAPTCHA implementation

Turnstile is designed with privacy in mind:

  • No cookies: Doesn't set any cookies, no consent banner needed
  • Minimal data: Only collects what's necessary for bot detection (IP, TLS fingerprint)
  • No advertising: Cloudflare explicitly prohibits using collected data for ads or tracking
  • GDPR-ready: Can be used without additional consent mechanisms

About this fork

Originally a fork of pixelopen/cloudflare-turnstile-bundle by Pixel Développement.

What's new:

  • Symfony 7.4 LTS and 8.x support
  • 22 languages for error messages
  • Customizable error messages via form options
  • disable_submit_until_verified option to prevent premature form submission
  • PHP 8.2+ with modern features (readonly, constructor promotion)
  • Active maintenance and community contributions welcome

Requirements

Requirement Version
PHP >= 8.2
Symfony >= 7.4

Installation

Step 1: Install the package

composer require vuillaume-agency/symfony-turnstile

Step 2: Register the bundle

Add the bundle to your config/bundles.php:

return [
    // ...
    VuillaumeAgency\TurnstileBundle\VuillaumeAgencyTurnstileBundle::class => ['all' => true],
];

Step 3: Add your Cloudflare credentials

Get your keys from the Cloudflare Dashboard and add them to your .env file:

TURNSTILE_KEY="your-site-key"
TURNSTILE_SECRET="your-secret-key"

That's it! The bundle automatically reads from these environment variables.

Optional: Custom configuration

If you need to customize the bundle, create config/packages/vuillaume_agency_turnstile.yaml:

vuillaume_agency_turnstile:
    key: '%env(TURNSTILE_KEY)%'      # Default: reads from TURNSTILE_KEY env var
    secret: '%env(TURNSTILE_SECRET)%' # Default: reads from TURNSTILE_SECRET env var
    enable: true                      # Default: true
    disable_submit_until_verified: false  # Default: false
Option Type Default Description
key string %env(TURNSTILE_KEY)% Your Turnstile site key (public)
secret string %env(TURNSTILE_SECRET)% Your Turnstile secret key (private)
enable boolean true Enable/disable validation
disable_submit_until_verified boolean false Disable submit buttons until Turnstile verification completes

Usage

Adding Turnstile to a form

Use TurnstileType in your form builder:

<?php

namespace App\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\FormBuilderInterface;
use VuillaumeAgency\TurnstileBundle\Type\TurnstileType;

class ContactType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder
            ->add('email', EmailType::class)
            ->add('message', TextareaType::class)
            ->add('captcha', TurnstileType::class, [
                'label' => false,
            ])
            ->add('submit', SubmitType::class);
    }
}

Widget customization

Customize the Turnstile widget appearance:

->add('captcha', TurnstileType::class, [
    'label' => false,
    'attr' => [
        'data-theme' => 'dark',      // 'light', 'dark', or 'auto'
        'data-size' => 'compact',    // 'normal' or 'compact'
        'data-action' => 'contact',  // Custom action name for analytics
    ],
])

See Cloudflare Turnstile documentation for all available options.

Disable submit until verified

To prevent form submission before Turnstile completes, enable automatic submit button disabling:

# config/packages/vuillaume_agency_turnstile.yaml
vuillaume_agency_turnstile:
    disable_submit_until_verified: true

When enabled:

  • Submit buttons are disabled on page load (server-side)
  • JavaScript enables them after Turnstile verification succeeds
  • Buttons are re-disabled if verification expires or fails

This improves UX by preventing users from clicking submit before the challenge is ready.

Important: This feature requires using SubmitType in your form builder. Raw HTML <button> elements in Twig templates will not be affected.

use Symfony\Component\Form\Extension\Core\Type\SubmitType;

$builder
    ->add('captcha', TurnstileType::class)
    ->add('submit', SubmitType::class, ['label' => 'Send']);

Custom error messages

Pass custom messages directly as form options:

->add('captcha', TurnstileType::class, [
    'label' => false,
    'missing_response_message' => 'Please verify you are human.',
    'verification_failed_message' => 'Verification failed, please try again.',
])

Or override translations in translations/validators.{locale}.yaml:

turnstile.missing_response: Please verify you are human.
turnstile.verification_failed: Verification failed. Please try again.

Translations

The bundle provides error messages in 22 European languages:

Language Code Language Code
English en Hungarian hu
French fr Swedish sv
Spanish es Danish da
German de Finnish fi
Italian it Norwegian nb
Portuguese pt Greek el
Dutch nl Czech cs
Polish pl Slovak sk
Romanian ro Slovenian sl
Bulgarian bg Lithuanian lt
Croatian hr Latvian lv
Ukrainian uk Estonian et
Turkish tr

Migrating from pixelopen/cloudflare-turnstile-bundle

This bundle is a modernized fork. Here's how to migrate:

Step 1: Replace the package

composer remove pixelopen/cloudflare-turnstile-bundle
composer require vuillaume-agency/symfony-turnstile

Step 2: Update bundles.php (this is probably automatically done by the composer remove and require)

// config/bundles.php
// Remove:
// PixelOpen\CloudflareTurnstileBundle\PixelOpenCloudflareTurnstileBundle::class => ['all' => true],

// Add:
VuillaumeAgency\TurnstileBundle\VuillaumeAgencyTurnstileBundle::class => ['all' => true],

Step 3: Delete old config

rm config/packages/pixel_open_cloudflare_turnstile.yaml

Step 4: Update imports in your code

Find and replace in your project:

Old New
PixelOpen\CloudflareTurnstileBundle\Type\TurnstileType VuillaumeAgency\TurnstileBundle\Type\TurnstileType
PixelOpen\CloudflareTurnstileBundle\Validator\CloudflareTurnstile VuillaumeAgency\TurnstileBundle\Validator\CloudflareTurnstile
# Quick find/replace with sed (Linux/macOS)
find src -name "*.php" -exec sed -i '' 's/PixelOpen\\CloudflareTurnstileBundle/VuillaumeAgency\\TurnstileBundle/g' {} +

Step 5: Clear cache

php bin/console cache:clear

Migrating from reCAPTCHA

Switching from Google reCAPTCHA is straightforward:

  1. Remove your reCAPTCHA bundle and configuration
  2. Install this bundle (see Installation)
  3. Replace RecaptchaType with TurnstileType in your forms
  4. Remove reCAPTCHA from your cookie consent banner
  5. Update your privacy policy (simpler now!)

No changes needed in your controllers — validation works the same way.

Testing

During development, use Cloudflare's test credentials instead of real keys. This is the recommended approach as it keeps validation active while providing predictable behavior.

Test site keys

Site key Behavior
1x00000000000000000000AA Always passes (recommended)
2x00000000000000000000AB Always blocks
3x00000000000000000000FF Forces an interactive challenge

Test secret keys

Secret key Behavior
1x0000000000000000000000000000000AA Always passes (recommended)
2x0000000000000000000000000000000AA Always fails
3x0000000000000000000000000000000AA Returns "token already spent"

Example dev configuration

# .env.local (for development)
TURNSTILE_KEY="1x00000000000000000000AA"
TURNSTILE_SECRET="1x0000000000000000000000000000000AA"

Alternative: Disabling validation

You can also disable validation entirely, but using test keys is preferred as it keeps your code paths consistent between environments:

# config/packages/dev/vuillaume_agency_turnstile.yaml
vuillaume_agency_turnstile:
    enable: false

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

Please ensure tests pass (vendor/bin/phpunit) and code follows standards (vendor/bin/php-cs-fixer fix).

License

The MIT License (MIT). See LICENSE for more information.