Your generic HMAC SHA-256 signature logic consistently fails when validating custom integrations.
Copy-paste verified examples. Use the tab that matches your stack.
const crypto = require('crypto');
app.post('/webhooks/custom', express.raw({ type: '*/*' }), (req, res) => {
const receivedSig = req.headers['x-webhook-signature'] || req.headers['x-signature'];
if (!receivedSig) return res.status(400).send('No signature header');
// Check with sender: is the signature hex or base64?
const encoding = 'hex'; // change to 'base64' if required by the provider
const expectedSig = crypto
.createHmac('sha256', process.env.WEBHOOK_SECRET)
.update(req.body)
.digest(encoding);
// Timing-safe comparison — both buffers must have the same encoding
let valid = false;
try {
const a = Buffer.from(receivedSig, encoding);
const b = Buffer.from(expectedSig, encoding);
valid = a.length === b.length && crypto.timingSafeEqual(a, b);
sectionTitles: {problem: 'Why Is Your Custom HMAC Webhook Validation Failing?', why: 'Root Causes of Custom HMAC Signature Mismatches', fix: 'How to Fix Custom HMAC Webhook Validation', code: 'Working Custom HMAC Webhook Validation Code', mistakes: 'Common Mistakes When Building Custom HMAC Webhooks', workflow: 'Custom HMAC Webhook Debugging Workflow', prevention: 'How to Prevent Custom HMAC Signature Errors'},
}, catch { valid = false; }
if (!valid) return res.status(401).send('Signature mismatch');
const payload = JSON.parse(req.body);
res.status(200).json({ ok: true });
});Determine encoding → read raw payload → generate HMAC → timing-safe compare.
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.