Validate X-Hub-Signature-256, add DLQ/replay, and stop losing events when deploys or timeouts happen.
Works with webhooks from
GitHub signs webhook payloads with X-Hub-Signature-256 when a secret is configured. This header contains an HMAC-SHA256 hash prefixed with sha256=.
The older X-Hub-Signature header uses SHA-1, which is legacy. GitHub recommends using SHA-256 for all new webhook implementations.
CI/CD downtime and deploy windows shouldn't mean lost GitHub webhooks.
Visual mismatch debugging for X-Hub-Signature-256 with clear reasons: algorithm, secret, or body differences.
Failed PR/push/issue events stored with full audit trail. One-click replay to staging or prod.
See exactly what happened, when. Response times, status codes, retry attempts—all visualized.
Add EventDock to your GitHub webhooks in under 5 minutes
Select GitHub as the provider and paste your upstream URL (CI/CD webhook handler, notification service, etc.).
Generate a strong secret in GitHub webhook settings. Paste the same secret into EventDock for signature verification.
In your GitHub repo/org settings → Webhooks, add the EventDock URL. Select events (push, PR, issues, etc.).
Trigger a webhook (push, open PR), view it in EventDock's Stream, and enable alerts for failed deliveries.
curl -s https://api.eventdock.app/in/<endpoint-id> \
-H "X-Hub-Signature-256: sha256=<hex-digest>" \
-H "Content-Type: application/json" \
--data '{"action":"opened","pull_request":{"id":123}}'
In EventDock, open Stream → verify signature verdict and replay to staging.
Validate X-Hub-Signature-256 correctly
Parse the sha256= prefix and perform constant-time hex comparison. CRITICAL: Decode hex strings to bytes before comparison.
const crypto = require('crypto');
function verifyGitHubSignature(payload, signatureHeader, secret) {
// Header format: "sha256="
if (!signatureHeader || !signatureHeader.startsWith('sha256=')) {
throw new Error('Invalid signature header format');
}
const sigHex = signatureHeader.slice(7); // Remove "sha256=" prefix
// Compute expected signature
const expectedHex = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
// CRITICAL: Decode hex strings to bytes for timing-safe comparison
const a = Buffer.from(sigHex, 'hex');
const b = Buffer.from(expectedHex, 'hex');
return a.length === b.length && crypto.timingSafeEqual(a, b);
}
// Usage in Express
app.post('/github-webhook', express.raw({ type: 'application/json' }), (req, res) => {
const signature = req.headers['x-hub-signature-256'];
const secret = process.env.GITHUB_WEBHOOK_SECRET;
try {
verifyGitHubSignature(req.body, signature, secret);
// Process webhook...
res.status(200).send('OK');
} catch (err) {
res.status(401).send('Unauthorized');
}
});
import hmac
import hashlib
def verify_github_signature(payload: bytes, signature_header: str, secret: str) -> bool:
"""Verify GitHub X-Hub-Signature-256."""
# Parse header (format: "sha256=")
if not signature_header or not signature_header.startswith('sha256='):
raise ValueError('Invalid signature header')
signature = signature_header[7:] # Remove "sha256=" prefix
# Compute expected signature
expected = hmac.new(
secret.encode('utf-8'),
payload,
hashlib.sha256
).hexdigest()
# Constant-time comparison
return hmac.compare_digest(expected, signature)
# Usage in Flask
@app.route('/github-webhook', methods=['POST'])
def github_webhook():
signature = request.headers.get('X-Hub-Signature-256')
secret = os.environ['GITHUB_WEBHOOK_SECRET']
if not verify_github_signature(request.data, signature, secret):
return 'Unauthorized', 401
# Process webhook...
return 'OK', 200
Prefer X-Hub-Signature-256 which uses SHA-256. The older X-Hub-Signature header uses SHA-1, which is legacy and less secure. GitHub recommends using SHA-256 for all new implementations.
Yes. EventDock allows one-click replay of any GitHub webhook to production or staging endpoints with full audit trail, making it easy to test PR/push/issue event handling without triggering real GitHub actions.
No. EventDock forwards original GitHub headers including User-Agent unchanged. We add X-EventDock-* metadata headers for tracing but preserve all original request headers and body.
Parse the sha256= prefix from the header, compute HMAC-SHA256 of the raw request body using your webhook secret, and perform a constant-time hex string comparison.
EventDock buffers the webhook and retries with exponential backoff until your CI/CD recovers. Failed events go to DLQ where you can replay them once your service is back online.
Yes. EventDock shows you exactly when each delivery attempt happened, the response time, status code, and any errors. This makes debugging GitHub webhook issues much faster.
5,000 events/month free. No credit card required. 5-minute setup.
Start Free Trial