GitHub webhooks fail validation with X-Hub-Signature-256 header mismatch errors.
Copy-paste verified examples. Use the tab that matches your stack.
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 });
});Extract header → strip prefix → hash payload → secure compare → process action.
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 testerWas this page helpful?
Your feedback helps us improve the docs.