Documentation Menu

GitHub Webhook Secret Validation Mismatch? Fix Guide

Problem Introduction

GitHub webhooks fail validation with X-Hub-Signature-256 header mismatch errors.

Why It Happens

  • Not stripping the "sha256=" prefix before compare
  • Invalid payload byte encoding (UTF-8 required)
  • Legacy SHA-1 header being used
  • No secret configured in GitHub repo settings

Step-by-Step Fix

  1. 1In your GitHub Repo → Settings → Webhooks, ensure a strong secret is set.
  2. 2Extract the X-Hub-Signature-256 header (not X-Hub-Signature).
  3. 3Strip the "sha256=" prefix from the received header value.
  4. 4Compute HMAC-SHA256 of the raw payload bytes using your secret.
  5. 5Use crypto.timingSafeEqual() — never use === for hash comparison.
  6. 6Return 200 immediately for "ping" events without processing.

Working Code

Copy-paste verified examples. Use the tab that matches your stack.

javascript
const crypto = require('crypto');

app.post('/webhooks/github', express.raw({ type: 'application/json' }), (req, res) => {
  const sigHeader = req.headers['x-hub-signature-256'];
  if (!sigHeader) return res.status(400).send('Missing signature');

  // Strip the "sha256=" prefix GitHub prepends
  const receivedSig = sigHeader.replace(/^sha256=/, '');

  const expectedSig = crypto
    .createHmac('sha256', process.env.GITHUB_WEBHOOK_SECRET)
    .update(req.body)   // raw Buffer from express.raw()
    .digest('hex');

  const a = Buffer.from(receivedSig, 'hex');
  const b = Buffer.from(expectedSig, 'hex');
  if (a.length !== b.length || !crypto.timingSafeEqual(a, b)) {
    return res.status(401).send('Signature mismatch');
    sectionTitles: {problem: 'Why Is Your GitHub Webhook Secret Validation Failing?', why: 'Root Causes of GitHub Webhook Signature Mismatches', fix: 'How to Fix GitHub X-Hub-Signature-256 Validation', code: 'Working GitHub Webhook Signature Validation Code', mistakes: 'Most Common GitHub Webhook Signature Mistakes', workflow: 'GitHub Webhook Debugging Workflow', prevention: 'How to Prevent GitHub Signature Errors'},
  },

  const event = req.headers['x-github-event'];
  if (event === 'ping') return res.status(200).send('pong');

  const payload = JSON.parse(req.body);
  console.log('GitHub event:', event, 'repo:', payload.repository?.full_name);
  res.status(200).json({ received: true });
});

Common Mistakes

  • Comparing "sha256=abcdef..." against "abcdef..." directly
  • Not setting a strong secret in Repo Settings
  • Failing on initial ping event

Debugging Workflow

Extract header → strip prefix → hash payload → secure compare → process action.

Preventive Best Practices

  • Use Hookmetry to pinpoint if the mismatch is due to prefix padding or body encoding

Works with webhooks and other async event systems (including AI callbacks). Instead of guessing, inspecting the exact payload and headers can help debug faster.

Try the free webhook tester

Was this page helpful?

Your feedback helps us improve the docs.