php 41 lines · 5 steps

Verifying GitHub webhook signatures in Laravel

A Laravel middleware that authenticates incoming GitHub webhooks by recomputing and comparing their HMAC signature.

Explained by highlit
1<?php
2 
3namespace App\Http\Middleware;
4 
5use Closure;
6use Illuminate\Http\Request;
7use Illuminate\Support\Facades\Log;
8use Symfony\Component\HttpFoundation\Response;
9 
10class VerifyGitHubSignature
11{
12 public function handle(Request $request, Closure $next): Response
13 {
14 $signature = $request->header('X-Hub-Signature-256');
15 
16 if (! $signature || ! str_starts_with($signature, 'sha256=')) {
17 Log::warning('GitHub webhook rejected: missing signature', [
18 'delivery' => $request->header('X-GitHub-Delivery'),
19 ]);
20 
21 abort(Response::HTTP_UNAUTHORIZED, 'Missing signature.');
22 }
23 
24 $expected = 'sha256=' . hash_hmac(
25 'sha256',
26 $request->getContent(),
27 (string) config('services.github.webhook_secret'),
28 );
29 
30 if (! hash_equals($expected, $signature)) {
31 Log::warning('GitHub webhook rejected: signature mismatch', [
32 'delivery' => $request->header('X-GitHub-Delivery'),
33 'event' => $request->header('X-GitHub-Event'),
34 ]);
35 
36 abort(Response::HTTP_UNAUTHORIZED, 'Invalid signature.');
37 }
38 
39 return $next($request);
40 }
41}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Webhook authenticity is proven by recomputing an HMAC over the raw body with a shared secret and comparing it to the sender's header.
  2. 2Always use a constant-time comparison like hash_equals for secrets to avoid leaking information through timing.
  3. 3Middleware is the natural place to reject unauthenticated requests before they ever reach your business logic.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Verifying GitHub webhook signatures in Laravel — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code