Free tool · runs in your browser
Webhook signature verifier: check a signature and see why it fails
webhook-inspect is an open-source tool that verifies Stripe, GitHub, Shopify, Slack, Twilio and generic HMAC webhook signatures, explains what each event is, and tells you exactly why a signature check fails.
Try webhook-inspect
Paste the signing secret, the request headers and the raw body. Verification uses your browser's WebCrypto; nothing you paste is sent anywhere.
The interactive tool needs JavaScript. The same core runs locally: npx @teamshift/webhook-inspect
Who it is for
Developers whose webhook endpoint answers "No signatures found matching the expected signature" or a bare 401, and anyone checking that an agent's inbound edge actually verifies who is calling it.
How it works
It reads the provider's signature header, rebuilds the exact string that provider signs, and computes the HMAC with your secret using WebCrypto, comparing in constant time. Stripe signs the timestamp and raw body with the full whsec_ secret; GitHub and Shopify sign the raw body; Slack signs a v0 base string with its timestamp; Twilio signs the full URL plus sorted form parameters.
When the check fails it tries concrete fixes and marks a cause as confirmed when a fix makes the signature verify: a body that was parsed and re-serialized, an added trailing newline, whitespace or quotes in the secret, a stripped whsec_ prefix, an API key used instead of the signing secret, a timestamp outside the replay window or in milliseconds, or a Twilio URL rewritten by a proxy.
Example
A Stripe event that a framework parsed and re-serialized before verification fails like this:
signature ✗ signature_mismatch
confirmed: The signature matches when the JSON is restored to 2-space
indentation and a trailing newline: the body was parsed and re-serialized
before verification.
→ Verify against the raw request body before any JSON parsing.Load the "Re-serialized body" sample above to reproduce it. The CLI adds listen to capture webhooks locally and replay to re-send them, re-signed with a fresh timestamp.
Limitations
- It verifies HMAC signature schemes for six providers plus a generic mode. Asymmetric schemes that use public-key signatures are not covered.
- You need the real signing secret and the body byte for byte. If your logs already re-serialized the body, the diagnosis can only tell you that it changed.
- Twilio checks need the exact public URL Twilio requested.
FAQ
How do I verify a Stripe webhook signature?
Read the Stripe-Signature header (t=timestamp,v1=signature) and compute HMAC-SHA256 over the timestamp, a period, and the raw body, keyed with the endpoint's full whsec_ secret. Compare the hex result to each v1 value in constant time and reject timestamps older than your tolerance; Stripe's libraries default to 300 seconds.
Why does my webhook signature verification fail?
Usually because the body changed before verification (a body parser re-serialized it or added a newline), the secret is wrong (another endpoint, a stripe listen session, an API key, or a trailing newline from .env), or the timestamp is outside the replay window.
Is it safe to paste a webhook secret here?
The page sends nothing: verification runs in your browser with WebCrypto, and no field is stored or uploaded. Even so, prefer a test-mode secret, and rotate any live secret you have pasted into a tool you do not control.
How do I get the raw request body in Express or Next.js?
In Express, mount express.raw({ type: "application/json" }) on the webhook route before express.json(). In Next.js route handlers and other Fetch-style runtimes, call await request.text() before anything parses the body, verify that string, and only then parse it.