Verify GitHub Webhook Signatures

GitHub signs webhook deliveries with HMAC-SHA256 over the raw request body and sends the result in the X-Hub-Signature-256 header (formatted sha256=…). Paste the raw body, the secret you configured on the webhook, and the header value.

Paste the X-Hub-Signature-256 value above to compare

Everything runs in your browser — the payload and secret never leave this page. Want to verify a different provider? See the webhook signature verifier hub or the generic HMAC generator.

How GitHub signs webhooks

  1. Take the raw request body exactly as delivered.
  2. Compute HMAC-SHA256 using your webhook secret as the key, hex-encoded.
  3. Prefix it with sha256= and compare to X-Hub-Signature-256 with a constant-time check.

Reference: GitHub signature documentation.

Verify GitHub signatures in code

Node.js
const crypto = require('crypto');

const expected = 'sha256=' + crypto
  .createHmac('sha256', process.env.WEBHOOK_SECRET)
  .update(rawBody)            // raw bytes of the request body
  .digest('hex');

const received = req.headers['x-hub-signature-256'];
const valid = crypto.timingSafeEqual(
  Buffer.from(expected), Buffer.from(received));
Python
import hmac, hashlib

expected = 'sha256=' + hmac.new(
    secret.encode(), raw_body, hashlib.sha256).hexdigest()

received = request.headers['X-Hub-Signature-256']
valid = hmac.compare_digest(expected, received)

Frequently asked questions

X-Hub-Signature vs X-Hub-Signature-256?

GitHub sends both. X-Hub-Signature is the legacy SHA-1 version; always verify X-Hub-Signature-256 (SHA-256) instead.

Where do I set the secret?

In the repository or organization webhook settings, in the "Secret" field. GitHub then signs every delivery with it; an empty secret means no signature is sent.

Verify other providers

Receiving GitHub webhooks on a server behind a firewall or on localhost? Webhook Relay can forward them to your internal service and even verify or transform them before delivery.