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.