Stripe: webhook signature verification failing [2026 fix]

Webhook signature mismatch occurs when endpoint secret is wrong or request body is altered. Verify exact secret from Dashboard and ensure raw request body is passed to verification.

Stripe: webhook signature verification failing [2026 fix]

TL;DR

Cause: Your webhook endpoint secret is mismatched, or the request body was parsed/modified before signature verification.

Fix: Use the exact secret from Stripe Dashboard's Webhook Settings, verify raw request body is passed to constructEvent(), and ensure middleware order doesn't modify the body.

---

Exact Console Error Messages

``` Error: No signatures found matching the expected signature for payload. at constructEvent (/node_modules/stripe/lib/resources/WebhookEndpoints.js:92:15) at Object.constructEvent [as default] (/app/webhooks/stripe.js:15:8) ```

``` error: Webhook signature verification failed: invalid signature timestamp=1672531200, signatures=["v1=abcd1234..."], secret=whsec_live_xxxx ```

``` Stripe.error.StripeSignatureVerificationError: No signatures found matching the expected signature for payload. at verifySignatureHeaders (/node_modules/stripe/lib/WebhookEndpoint.js:118:22) ```

``` Error: Webhook endpoint received request with mismatched signature Expected: v1=xyz789 Received: v1=abc123 Timestamp: 1672531234567 ```

``` TypeError: Cannot read property 'id' of undefined at webhook handler (Signature verification passed but event object is null) ```

---

Broken Code vs. Fixed Code

❌ BROKEN: Using parsed JSON body

```javascript // app.js const bodyParser = require('body-parser'); const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);

app.use(bodyParser.json());

app.post('/webhook', (req, res) => { // WRONG: body is already parsed const sig = req.headers['stripe-signature']; try { const event = stripe.webhooks.constructEvent( req.body, // ← PROBLEM: this is Object, not raw string sig, process.env.STRIPE_WEBHOOK_SECRET ); res.json({received: true}); } catch (err) { res.status(400).send(Webhook Error: ${err.message}); } }); ```

✅ FIXED: Using raw request body before parsing

```javascript // app.js const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);

// Apply JSON parser AFTER webhook route app.post('/webhook', express.raw({type: 'application/json'}), (req, res) => { const sig = req.headers['stripe-signature']; try { const event = stripe.webhooks.constructEvent( req.body, // ← CORRECT: raw Buffer from express.raw() sig, process.env.STRIPE_WEBHOOK_SECRET ); // Now safe to handle event if (event.type === 'payment_intent.succeeded') { console.log('Payment successful:', event.data.object.id); } res.json({received: true}); } catch (err) { console.error('Webhook signature verification failed:', err.message); res.status(400).send(Webhook Error: ${err.message}); } });

// NOW apply body parser for other routes app.use(express.json()); ```

Environment Variable Verification

```bash

❌ WRONG: Using publishable key or incorrect secret

STRIPE_WEBHOOK_SECRET=pk_live_xxxx # NEVER use publishable key STRIPE_WEBHOOK_SECRET=whsec_test_xxxx # test key in production

✅ CORRECT: Exact secret from Dashboard → Webhooks → Click endpoint

STRIPE_WEBHOOK_SECRET=whsec_live_abcdef1234567890 # Copy from Dashboard exactly ```

---

Why This Happens

1. Middleware order matters: bodyParser.json() or express.json() converts raw request body to JavaScript object. Stripe's signature verification requires the exact raw bytes that were signed.

2. Wrong environment variable: Copy-pasting the wrong secret from Dashboard (test vs. live, or wrong endpoint).

3. Request body modification: Logging middleware, authentication middleware, or custom parsers that touch req.body before webhook handler.

4. Version confusion: Stripe.js v3+ changed signature verification behavior. [We've documented the Node SDK update process](/?guide=stripe-sdk-migration).

---

Still broken? Check these too

1. Webhook secret mismatch between environments – Verify you copied the secret for the correct environment (test vs. live) and correct endpoint URL from Stripe Dashboard.

2. Request signing timestamp too old – Stripe rejects signatures older than 5 minutes by default. Check server time sync: date && curl -I https://www.stripe.com/ to verify server clock.

3. Raw body middleware not applied correctly – If using frameworks like NestJS or Express with custom middleware, ensure express.raw({type: 'application/json'}) wraps the webhook route before any JSON parsing. [See middleware ordering guide](/?guide=express-middleware-order).

---

Verification Checklist

  • [ ] Secret starts with whsec_ (not sk_, pk_, or rk_)
  • [ ] Using express.raw() on webhook route only, not globally
  • [ ] No JSON parsing middleware before webhook handler
  • [ ] Webhook endpoint URL in Stripe Dashboard matches your actual route
  • [ ] Server time is synced (within 5 minutes of NTP)
  • [ ] Signature header present: stripe-signature in request headers
  • ---

    Official Resources

  • [Stripe Webhooks Documentation](https://stripe.com/docs/webhooks)
  • [Stripe.js Node.js Library - Webhook Signature Verification](https://github.com/stripe/stripe-node#webhook-signing)
  • [Stripe Dashboard - Webhook Endpoints](https://dashboard.stripe.com/webhooks)
  • ---

    Found a different variation? Drop it in the comments – Stripe's webhook auth behavior varies by SDK version and framework. Help us build a complete troubleshooting resource.

    🔥 0d
    LIVE
    PlanetScale rage spiking Vercel pricing complaints Railway gaining fast Supabase happiness rising Resend loved by devs PlanetScale rage spiking Vercel pricing complaints Railway gaining fast Supabase happiness rising
    DEVELOPER PAIN RADAR // Loading...

    Developers complain.
    Opportunities appear.

    We track what developers are struggling with today — and what opportunities that creates.

    guides today
    avg happiness
    🔥 Pain
    📖 Guides
    🔭 Explore
    👤 Mine
    🔥 Pain Radar — rage scores today
    ↗ share
    💡 Opportunity Feed — pain = market gap
    📈 Tool Momentum
    all scores →
    📖 Latest Guide
    all guides →
    📖 All Guides
    📊 Tool Scores
    + Submit
    📰 Hacker News
    ➕ Submit a Tool
    ← back