Verify Stripe Webhook Signatures

Stripe signs every webhook with an HMAC-SHA256 over a timestamped payload and sends it in the Stripe-Signature header (e.g. t=1614265330,v1=…). Verifying it prevents spoofed events and replay attacks. Paste the raw request body, your endpoint signing secret and the header below.

Paste the Stripe-Signature value above to compare

Everything runs in your browser — the payload and secret never leave this page. Want to verify a different provider? See the webhook signature verifier hub or the generic HMAC generator.

How Stripe signs webhooks

  1. Read the t (timestamp) and v1 (signature) values from the Stripe-Signature header.
  2. Build the signed payload string: the timestamp, a literal ., then the raw request body.
  3. Compute HMAC-SHA256 of that string using your endpoint signing secret (whsec_…) as the key, hex-encoded.
  4. Compare it to v1 with a constant-time check, and reject events whose timestamp is too old.

Reference: Stripe signature documentation.

Verify Stripe signatures in code

Node.js
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);

// req.body must be the RAW body (Buffer/string), not parsed JSON.
const sig = req.headers['stripe-signature'];
try {
  const event = stripe.webhooks.constructEvent(
    req.body, sig, process.env.STRIPE_WEBHOOK_SECRET);
  // event is verified — handle it
} catch (err) {
  return res.status(400).send(`Webhook Error: ${err.message}`);
}
Python
import stripe

sig_header = request.headers['Stripe-Signature']
try:
    event = stripe.Webhook.construct_event(
        request.data, sig_header, endpoint_secret)
    # event is verified — handle it
except stripe.error.SignatureVerificationError:
    return '', 400

Frequently asked questions

What is the Stripe-Signature header?

It is the header Stripe adds to every webhook, formatted like t=1614265330,v1=5257a8…. t is the unix timestamp and v1 is the HMAC-SHA256 signature you verify.

Why does verification fail even with the right secret?

Almost always because the body was re-serialized. Stripe signs the exact raw bytes it sent, so you must verify against the unparsed request body — not JSON.stringify(req.body).

Which secret do I use?

The endpoint signing secret that starts with whsec_, shown in the Stripe Dashboard under Developers → Webhooks for that specific endpoint. It is not your API key.

Verify other providers

Receiving Stripe webhooks on a server behind a firewall or on localhost? Webhook Relay can forward them to your internal service and even verify or transform them before delivery.