Skip to content
cascadehook.

ToolsFree tool

GitHub webhook signature verifier

Check an X-Hub-Signature-256 header against your webhook's secret and see why verification fails. Nothing you paste leaves your browser.

Paste request headers

Paste the headers from a log, a request inspector, or curl -v, as Name: value lines or JSON. The matching fields below are filled in.

Signature your code computed (optional)

If your code rejects a request that verifies here, print the signature it computes for this request and paste it. The tool finds which mistake produces that value.

Runs entirely in your browser with Web Crypto. Nothing you paste is sent anywhere or saved.

The result shows up here

Paste the webhook's secret, the X-Hub-Signature-256 header, and the raw payload from a delivery, then verify. To see how it works first, use Try an example.

What gets signed

HMAC-SHA256(secret, body)

The body was parsed before it was verified

GitHub signs the exact payload bytes. Middleware such as express.json() hands you an object, and serializing it again rarely reproduces those bytes. Read the raw body, verify it, then parse it. Payloads can contain Unicode, so handle the bytes as UTF-8.

The webhook sends form data

With the content type set to application/x-www-form-urlencoded, GitHub signs the form body: payload= with the JSON URL-encoded inside it. Verifying the JSON you read out of the payload field fails. Verify the raw form body, or set the webhook's content type to application/json.

The wrong secret

Repository, organization, and GitHub App webhooks each have their own secret. If a webhook has no secret at all, GitHub sends no signature header.

The legacy header was used

X-Hub-Signature carries an HMAC-SHA1 signature and exists only for compatibility; X-Hub-Signature-256 uses HMAC-SHA256. Computing one algorithm and comparing against the other header always fails.

The prefix was dropped on one side

The header is sha256= followed by the hex digest. Either compare the whole header with "sha256=" + digest, or strip the prefix and compare the digests. Mixing the two never matches.

Something between GitHub and your code changed the body

A proxy, load balancer, or API gateway that re-encodes the body changes the bytes your code sees. Verify where the original request arrives, or make sure anything in front of it passes the body through untouched.

Sending webhooks from your own product? Cascadehook signs every delivery with Standard Webhooks, retries failures for up to 72 hours, and keeps every attempt for replay. Create a project or try the Standard Webhooks verifier.

GitHub is a trademark of GitHub, Inc.. Cascadehook isn't affiliated with or endorsed by GitHub.