GitHub webhooks
GitHub webhook failures are usually the wrong signature header, a secret mismatch, or a mutated body. There is also one structural limit worth knowing before you plan a production setup.
Create a catchbin endpoint with the GitHub provider. Add its URL as a webhook on a repository (Settings → Webhooks) or an organization, set the content type to application/json, and set a secret. Paste the same secret into the catchbin endpoint. catchbin verifies each delivery and stores the secret encrypted.
How GitHub signs
Section titled “How GitHub signs”GitHub sends an HMAC-SHA256 digest of the raw request body, hex-encoded, prefixed with sha256=:
X-Hub-Signature-256: sha256=757107ea0eb2509fc211221cce984b8a37570b6d7586c22c46f4379c8b043e17There is no timestamp and no built-in replay protection. GitHub also sends a legacy X-Hub-Signature (HMAC-SHA1) for backward compatibility — verify against X-Hub-Signature-256, not the sha1 header.
Quick answer
Section titled “Quick answer”| Symptom | Cause | Fix |
|---|---|---|
Verifying X-Hub-Signature (no -256) |
Using the deprecated sha1 header | Verify X-Hub-Signature-256 |
| Fails on every event immediately | Secret mismatch | Match the webhook secret exactly |
| Byte length differs from what GitHub sent | Body mutated | Verify the raw bytes |
| Can only configure one webhook URL | GitHub App single-URL limit | Fan out in your backend, or use repo/org webhooks |
Using the deprecated sha1 header
Section titled “Using the deprecated sha1 header”Symptom. Verification fails, and your code reads X-Hub-Signature.
Why. X-Hub-Signature is HMAC-SHA1, kept only for old integrations. New code should use X-Hub-Signature-256. A verifier that hashes with SHA256 but reads the SHA1 header will never match.
Fix. Read X-Hub-Signature-256 and compare against a SHA256 HMAC of the raw body. catchbin’s GitHub strategy uses the -256 header.
Secret mismatch
Section titled “Secret mismatch”Symptom. Total failure from the first event.
Why. The secret configured on the GitHub webhook differs from the one catchbin holds — a trailing space, a copy from the wrong environment, or a webhook created without a secret at all.
Fix. Re-enter the same secret on both sides. If the GitHub webhook has no secret, GitHub sends no signature header and verification cannot succeed — add one.
Body mutated
Section titled “Body mutated”Symptom. Nothing ever verified on a new endpoint, secret confirmed.
Why. Same as every provider: the bytes GitHub signed differ from the bytes your verifier hashed, because a proxy or parser rewrote the body.
Fix. Hash the raw request body before any JSON parsing. catchbin surfaces the received body byte count so you can spot a mismatch against what GitHub sent.
GitHub App single-webhook-URL limit
Section titled “GitHub App single-webhook-URL limit”Redelivery vs replay
Section titled “Redelivery vs replay”GitHub can redeliver a webhook from its own UI, which re-runs the whole delivery. catchbin replay is different: it re-sends a captured event to a target you choose, so you can point it at localhost or staging without touching GitHub. For a target that verifies signatures, use --resign with that target’s secret.