SafeWebhook/ Guides
Launch Workbench
LaravelPHP

Laravel Webhook Guide: CSRF Exclusion & spatie/laravel-webhook-client

Learn how to build robust webhook controllers in Laravel. Exclude routes from CSRF verification and validate HMAC signatures using Spatie Webhook Client.

Direct Answer / Quick Implementation Guide

In Laravel, add your webhook route `api/webhook/*` to the `$except` array in `app/Http/Middleware/VerifyCsrfToken.php` (or `bootstrap/app.php` in Laravel 11). Access raw content via `$request->getContent()` for HMAC signature validation.

Essential Implementation Takeaways

  • Exclude webhook route in `VerifyCsrfToken` middleware.
  • Use `$request->getContent()` to retrieve unmodified raw string.
  • Use `hash_equals()` for timing-safe signature comparison.
  • Dispatch background jobs using Laravel Queues.

Common Gotchas in Laravel & How to Fix Them

CSRF token mismatch 419 error
Fix:Add route to `$except` in CSRF middleware or register in `routes/api.php`.

Complete Production Boilerplate

PHP
<?php
// app/Http/Controllers/WebhookController.php (Laravel 10/11)

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;

class WebhookController extends Controller
{
    public function handle(Request $request): JsonResponse
    {
        $rawPayload = $request->getContent(); // Raw body string
        $signature = $request->header('X-Signature');
        $secret = config('services.webhook.secret');

        if (!$signature) {
            return response()->json(['error' => 'Missing signature'], 400);
        }

        $expectedSignature = hash_hmac('sha256', $rawPayload, $secret);

        if (!hash_equals($signature, $expectedSignature)) {
            return response()->json(['error' => 'Invalid signature'], 401);
        }

        $data = json_decode($rawPayload, true);
        
        // Dispatch job: ProcessWebhookEvent::dispatch($data);

        return response()->json(['status' => 'success'], 200);
    }
}

Step-by-Step Setup Guide

1
Create controller: `php artisan make:controller WebhookController`.
2
Register route in `routes/api.php` (exempt from CSRF by default).
3
Retrieve raw body via `$request->getContent()`.
4
Compare signatures with `hash_equals()`.
5
Return `response()->json(["status" => "success"], 200)`.

How to Test Locally on Your Machine

  • Run `php artisan serve` (running on http://localhost:8000).
  • Use SafeWebhook Replay Drawer to send captured events to `http://localhost:8000/api/webhook`.

Frequently Asked Questions: Laravel Webhooks

Why should I use hash_equals() in PHP?

`hash_equals()` provides timing-safe string comparison to protect against timing attacks in signature validation.

Test Your Laravel Webhook Handler Live

Capture payloads, simulate errors, and replay directly to your localhost server.

Launch Webhook Workbench →

Explore Other Webhook Framework Guides