Verify GitHub Webhook Payloads
Implement GitHub webhook payload verification using HMAC-SHA256. Learn GitHub's X-Hub-Signature-256 header format and secure verification patterns for CI/CD pipelines.
Detailed Explanation
Verifying GitHub Webhook Payloads
GitHub uses HMAC-SHA256 to sign webhook payloads, sending the signature in the X-Hub-Signature-256 header. This allows your server to confirm that webhook events originate from GitHub and have not been modified in transit.
GitHub's Signing Format
When you configure a webhook secret in a GitHub repository or organization:
- Secret configuration: You set a secret string when creating the webhook
- Signature computation: GitHub computes
HMAC_SHA256(secret, raw_body)for each delivery - Header format:
X-Hub-Signature-256: sha256=a3f2b8c1d4e5f6... - Legacy header:
X-Hub-Signature: sha1=...(HMAC-SHA1, deprecated)
Implementation Example
# Node.js verification
const crypto = require('crypto');
function verifyGitHubWebhook(secret, payload, signatureHeader) {
const expected = 'sha256=' +
crypto.createHmac('sha256', secret)
.update(payload, 'utf8')
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signatureHeader),
Buffer.from(expected)
);
}
Key Differences from Stripe
Unlike Stripe, GitHub's scheme:
- No timestamp in signature: GitHub does not include a timestamp in the signed payload. Replay protection must be handled separately (e.g., via delivery IDs).
- Simple format: The signature is always
sha256=followed by the hex-encoded HMAC. No parsing of multiple fields is needed. - Delivery ID: GitHub includes
X-GitHub-Deliveryheader with a unique UUID for each delivery, which you can store to detect replays. - Event type: The
X-GitHub-Eventheader identifies the event type (push, pull_request, etc.) but is not included in the signature — it should not be trusted without verifying the payload.
Securing CI/CD Pipelines
GitHub webhooks are commonly used to trigger CI/CD pipelines:
- Always verify signatures before triggering builds or deployments
- Validate the event type by checking the parsed payload (not just the header)
- Restrict branch patterns — do not blindly deploy from any branch mentioned in a webhook
- Log delivery IDs for audit trails and replay detection
- Use separate secrets for different webhook endpoints (production vs staging)
Migration from SHA-1
GitHub still sends the X-Hub-Signature header (HMAC-SHA1) for backward compatibility, but all new implementations should use X-Hub-Signature-256. If you are maintaining an older integration, updating is straightforward — change sha1 to sha256 in both the HMAC computation and the header comparison.
Use Case
GitHub webhook verification is essential for CI/CD pipelines, automated code review bots, deployment automation, issue tracking integrations, and any system that reacts to repository events like pushes, pull requests, or releases.