Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Turnstile Laravel Package

coderflex/laravel-turnstile

Add Cloudflare Turnstile CAPTCHA to Laravel with minimal setup. Includes config publishing, env-based site/secret keys, validation integration, and customizable/translatable error messages for protecting forms and endpoints from bots.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Install the package:
    composer require coderflex/laravel-turnstile
    
  2. Publish config (optional but recommended):
    php artisan vendor:publish --tag="turnstile-config"
    
  3. Add Turnstile keys to .env:
    TURNSTILE_SITE_KEY=your_site_key
    TURNSTILE_SECRET_KEY=your_secret_key
    
  4. Use the widget in a Blade form:
    <x-turnstile-widget />
    

First Use Case: Protecting a Contact Form

<form method="POST" action="/contact">
    @csrf
    <input type="text" name="name" required>
    <x-turnstile-widget theme="auto" />
    <button type="submit">Send</button>
</form>
// In your controller
use Coderflex\LaravelTurnstile\Facades\LaravelTurnstile;

public function store(Request $request) {
    $validated = $request->validate([
        'name' => 'required|string',
        'cf-turnstile-response' => ['required', new \Coderflex\LaravelTurnstile\Rules\TurnstileCheck()]
    ]);

    // Proceed if validation passes
}

Implementation Patterns

1. Widget Integration Patterns

  • Dynamic Theming: Use theme="auto" for system preference detection or hardcode theme="dark" for consistency:
    <x-turnstile-widget theme="dark" language="{{ app()->getLocale() }}" />
    
  • Conditional Rendering: Only show Turnstile on high-risk forms:
    @if(request()->is('register') || request()->is('contact'))
        <x-turnstile-widget />
    @endif
    
  • Custom Callback Handling: For AJAX forms, use JavaScript callbacks:
    <x-turnstile-widget callback="handleTurnstileSuccess" errorCallback="handleTurnstileError" />
    
    function handleTurnstileSuccess(token) {
        document.getElementById('turnstile-token').value = token;
    }
    

2. Validation Workflows

  • Inline Validation (Recommended for Forms):
    $request->validate([
        'cf-turnstile-response' => [new TurnstileCheck(), 'required'],
    ]);
    
  • Facade-Based Validation (For Non-Form Logic):
    $response = LaravelTurnstile::validate($request->input('cf-turnstile-response'));
    if (!$response['success']) {
        return back()->withErrors(['captcha' => trans('turnstile::turnstile.error')]);
    }
    
  • Bulk Validation: Validate multiple responses (e.g., for multi-step forms):
    $responses = ['step1' => $request->input('step1_captcha'), 'step2' => $request->input('step2_captcha')];
    foreach ($responses as $token) {
        if (!LaravelTurnstile::validate($token)['success']) {
            return back()->withErrors(['captcha' => 'Invalid CAPTCHA']);
        }
    }
    

3. API Integration Patterns

  • Custom API Endpoint: For headless apps or APIs:
    public function verifyCaptcha(Request $request) {
        $response = LaravelTurnstile::validate($request->input('token'));
        return response()->json($response);
    }
    
  • Queue Delayed Validation: For async processing:
    ValidateTurnstileJob::dispatch($request->input('cf-turnstile-response'))
        ->delay(now()->addMinutes(5));
    

4. Testing Patterns

  • Unit Testing Validation:
    public function test_validation_fails_with_invalid_token() {
        $rule = new TurnstileCheck();
        $this->assertFalse($rule->passes('data', 'invalid_token'));
    }
    
  • Feature Testing Widget Rendering:
    public function test_widget_renders_correctly() {
        $view = view('vendor.turnstile::widget', ['theme' => 'dark']);
        $view->assertSee('data-theme="dark"');
    }
    
  • Testing with Dummy Keys: Use Cloudflare’s test keys in .env.testing:
    TURNSTILE_SITE_KEY=0x4AF50000ABCDEFCD
    TURNSTILE_SECRET_KEY=0x4AF50000ABCDEFCD
    

Gotchas and Tips

Common Pitfalls

  1. Missing CSRF Token:

    • Turnstile’s response must be submitted with @csrf in Blade forms. Omitting it may cause silent validation failures.
    • Fix: Always include @csrf in forms with the widget.
  2. Case-Sensitive Field Names:

    • The package expects cf-turnstile-response (case-sensitive). Using CF-TURNSTILE-RESPONSE or cfTurnstileResponse will fail validation.
    • Fix: Stick to cf-turnstile-response in your form fields.
  3. Secret Key Exposure:

    • The TURNSTILE_SECRET_KEY in .env is not masked by default in Laravel’s .env files. Ensure it’s not committed to version control.
    • Fix: Add .env to .gitignore and use environment variables in CI/CD.
  4. Rate Limiting:

    • Cloudflare Turnstile has rate limits. Exceeding limits (e.g., >100 requests/minute) may return 429 Too Many Requests.
    • Fix: Implement exponential backoff in your validation logic or cache responses.
  5. JavaScript Dependency:

    • The widget requires Cloudflare’s JavaScript SDK. If your app uses no-JS modes (e.g., server-side rendering), the widget won’t render.
    • Fix: Use a fallback or server-side validation only for critical paths.

Debugging Tips

  1. Enable Debug Logging: Add to config/turnstile.php:

    'debug' => env('TURNSTILE_DEBUG', false),
    

    Then check storage/logs/laravel.log for validation details.

  2. Inspect API Responses: Cloudflare returns detailed errors in the error-codes field. Log the full response:

    $response = LaravelTurnstile::validate($token);
    \Log::debug('Turnstile Response:', $response);
    
  3. Test with Dummy Keys: Use Cloudflare’s test keys to verify integration before going live:

    TURNSTILE_SITE_KEY=0x4AF50000ABCDEFCD
    TURNSTILE_SECRET_KEY=0x4AF50000ABCDEFCD
    

Extension Points

  1. Custom Error Messages: Override the default message in config/turnstile.php:

    'error_messages' => [
        'turnstile_check_message' => 'Please complete the security verification.',
    ],
    

    Or translate it in your language files:

    'validation' => [
        'attributes' => [
            'cf-turnstile-response' => 'security check',
        ],
        'custom' => [
            'cf-turnstile-response' => [
                'turnstile' => 'The :attribute verification failed. Please try again.',
            ],
        ],
    ],
    
  2. Custom Validation Logic: Extend the TurnstileCheck rule for additional rules (e.g., IP-based allowlists):

    use Coderflex\LaravelTurnstile\Rules\TurnstileCheck;
    
    class ExtendedTurnstileCheck extends TurnstileCheck {
        public function passes($attribute, $value) {
            if ($this->isAllowedIP(request()->ip())) {
                return true;
            }
            return parent::passes($attribute, $value);
        }
    
        protected function isAllowedIP($ip) {
            // Your logic here
        }
    }
    
  3. Widget Customization: Publish the views and modify resources/views/vendor/turnstile/widget.blade.php to:

    • Change the container class (e.g., for Tailwind CSS).
    • Add custom attributes (e.g., data-testid="turnstile" for testing).
  4. Event Listeners: Listen for validation events to log attempts or trigger analytics:

    use Coderflex\LaravelTurnstile\Events\TurnstileValidated;
    
    TurnstileValidated::listen(function ($event) {
        \Log::info('Turnstile validation', $event->response);
    });
    

Performance Optimizations

  1. Cache Validation Responses: For high-traffic forms, cache responses to avoid repeated API calls:
    $cacheKey
    
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
craftcms/url-validator
directorytree/privacy-filter-classifier
directorytree/privacy-filter
datacore/hub-sdk
develia/commons
cuci/prototurk-sdk
cuci/prototurk-sdk-symfony
develia/geo-bundle
dreamzy/livewire-charts
touchestate-sdk/php-sdk
22h/doctrine-garbage-collection-bundle
agtp/agtp-php
agtp/mod-php
splash/sonata-admin
splash/metadata
splash/openapi
splash/scopes
splash/toolkit
testo/output-teamcity
testo/bridge-symfony