GitHub Webhook Not Working? A Systematic Debugging Guide
GitHub webhooks failing silently is one of the most common developer frustrations. Here's a systematic approach to finding out what's actually going wrong — and fixing it.
Jason Warner
March 20, 2026
GitHub Webhook Not Working? A Systematic Debugging Guide
GitHub webhook failures are frustratingly opaque. The event fires, something goes wrong somewhere in the delivery chain, and you have no idea what happened. Here's a systematic approach to tracking down the problem.
Step 1: Check GitHub's Delivery Log
GitHub logs every webhook delivery attempt. This is always your first stop.
For repository webhooks:
- Go to your repo → Settings → Webhooks
- Click on the webhook
- Click Recent Deliveries
For organization webhooks: Organization Settings → Webhooks → click the webhook → Recent Deliveries
For GitHub Apps: Developer Settings → GitHub Apps → your app → Advanced → Recent Deliveries
Each delivery shows the event type, delivery timestamp, whether it succeeded, and the full request payload and the response your endpoint returned.
Look at the Response tab. Is it a 200? A 4xx? A 5xx? A timeout? This tells you where the problem is.
Step 2: Diagnose by Response Code
No response / timeout: Your endpoint isn't reachable. Is the URL correct? Is your server running? If you're on localhost, are you using a tunnel?
404 Not Found: The URL path doesn't match any route in your application.
401 / 403: Your endpoint is rejecting the request. Common causes: signature verification rejecting valid GitHub signatures, IP allowlisting blocking GitHub's IPs.
500 Internal Server Error: Your handler threw an exception. Check your application logs.
200 but nothing happened: Your handler returned 200 but didn't process the event. Check whether the event type you configured matches what GitHub is sending, and whether your handler is correctly reading the X-GitHub-Event header.
Step 3: Signature Verification Issues
GitHub signs every webhook with a SHA-256 HMAC. If you're getting 401 or processing some events but not others, verification is often the cause.
Verify your secret matches exactly. No trailing spaces, no encoding differences.
Use the raw request body. Signature verification must run on the raw, unmodified body. If your framework parses JSON before verification, the signature will fail.
// Express — correct
app.post('/github/webhook', express.raw({ type: 'application/json' }), (req, res) => {
const sig = req.headers['x-hub-signature-256'];
const hmac = crypto.createHmac('sha256', process.env.GITHUB_SECRET);
hmac.update(req.body); // raw buffer, not parsed JSON
const expected = 'sha256=' + hmac.digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected))) {
return res.sendStatus(401);
}
// process event
});
Use timing-safe comparison. Always use crypto.timingSafeEqual or equivalent — not ===.
Step 4: Event Type Filtering
GitHub sends a X-GitHub-Event header with every delivery. If your handler only processes specific event types (e.g., push), but you've subscribed to others (pull_request), those others will arrive and return 200 without being processed.
Check Recent Deliveries — it shows the event type for each delivery.
Step 5: Local Development
If testing GitHub webhooks locally, GitHub can't reach localhost. Options:
- ngrok:
ngrok http 3000gives you a public URL - Bluejay Relay: Set up a permanent integration URL, point GitHub at that URL, add your local server as a destination. Events are logged in Bluejay Relay even when your local server is down — replay them when you're back up.
Step 6: Use GitHub's Redeliver Feature
Once you've fixed the problem, use the Redeliver button on any failed delivery in Recent Deliveries. This resends the exact same payload without you needing to trigger a new event.
Checklist
- GitHub Recent Deliveries shows a response code
- URL is correct and publicly accessible
- Secret in GitHub matches secret in your code exactly
- Raw body used for signature verification (not parsed JSON)
-
X-GitHub-Eventheader is being read - Correct event types are subscribed in webhook settings
Using Bluejay Relay as your GitHub webhook endpoint gives you a permanent delivery log and replay capability. Start free.
Build more reliable webhook workflows.
Capture, transform, and retry webhooks with full observability. Free to start, no credit card.