Stripe: webhook signature verification failing [2026 fix]

Webhook signature mismatch: endpoint secret doesn't match request signature. Verify you're using raw request body, not parsed JSON.

Stripe: Webhook Signature Verification Failing [2026 Fix]

TL;DR

Cause: You're passing parsed JSON to signature verification instead of the raw request body.

Fix: Use express.raw({type: 'application/json'}) middleware BEFORE body parsing for that route.

---

Common Error Messages

Here are exact console outputs you might see:

``` Error: No signatures found matching the expected signature for payload. ```

``` Stripe error [ERR_SIGNATURE_VERIFICATION_FAILED]: Webhook signature verification failed, possibly due to changed API version or endpoint secret ```

``` error: 'sig_' does not match webhook signature 'v1,t=1234567890,v1=abc123def456' ```

``` SignatureVerificationError: Unable to extract timestamp and signatures from header 'stripe-signature' ```

``` WebhookEndpointDoesNotExist: Webhook endpoint secret is invalid or does not exist ```

---

Broken Code vs. Fix

Problem: Body Already Parsed

```javascript // ❌ BROKEN const express = require('express'); const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);

const app = express();

// This parses ALL requests including webhooks app.use(express.json());

app.post('/webhook', (req, res) => { const sig = req.headers['stripe-signature']; const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET; try { // req.body is already stringified JSON object // Stripe can't verify signature on parsed object const event = stripe.webhooks.constructEvent( req.body, sig, webhookSecret ); res.json({received: true}); } catch (error) { res.status(400).send(Webhook Error: ${error.message}); } }); ```

Solution: Raw Body for Webhook Route

```javascript // ✅ FIXED const express = require('express'); const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);

const app = express();

// Parse JSON for all routes EXCEPT webhooks app.use(express.json());

// Use raw body ONLY for webhook route app.post( '/webhook', express.raw({type: 'application/json'}), (req, res) => { const sig = req.headers['stripe-signature']; const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET; try { // req.body is now Buffer (raw bytes) // Stripe can verify signature correctly const event = stripe.webhooks.constructEvent( req.body, sig, webhookSecret ); // Handle event switch (event.type) { case 'payment_intent.succeeded': console.log('Payment succeeded:', event.data.object.id); break; case 'payment_intent.payment_failed': console.log('Payment failed:', event.data.object.id); break; } res.json({received: true}); } catch (error) { res.status(400).send(Webhook Error: ${error.message}); } } );

app.listen(3000); ```

Key difference: The raw middleware must come AFTER app.use(express.json()) but apply only to the webhook endpoint.

---

Quick Verification Checklist

1. Webhook Secret: Confirm STRIPE_WEBHOOK_SECRET matches the secret shown in Stripe Dashboard → Developers → Webhooks → your endpoint's signing secret (starts with whsec_)

2. Raw Body Middleware: Ensure express.raw({type: 'application/json'}) is applied to your webhook route before signature verification

3. Request Body Type: Log typeof req.body — should be object for Buffer, not string

4. Test Webhook: Use Stripe's test event in Dashboard (Webhooks → Send test event) rather than manual curl requests

---

Still Broken? Check These Too

1. Proxy/Reverse Proxy Buffering Raw Body

Some reverse proxies (nginx, CloudFlare Workers) buffer and re-encode the request body. Verify: Enable request logging to see if body content changes between Stripe's send and your receipt. Check proxy documentation for raw body passthrough settings.

2. Multiple Webhook Endpoints with Wrong Secret

You might have registered the webhook endpoint in Dashboard with one secret, but your code references a different secret from .env. Verify: Log both webhookSecret variable and Stripe Dashboard value character-by-character.

3. Framework Auto-Parsing Middleware

Other middleware (body-parser, fastify, Next.js API routes) may auto-parse before reaching your handler. Verify: Disable middleware selectively and test. For Next.js, see [Stripe Next.js troubleshooting](/?guide=nextjs-stripe).

---

Environment Setup

```bash

.env

STRIPE_SECRET_KEY=sk_live_... STRIPE_WEBHOOK_SECRET=whsec_... ```

Note: If uncertain whether your specific Stripe SDK version requires additional configuration beyond raw body handling, check the [official Stripe webhook docs](https://stripe.com/docs/webhooks/signatures) for your language.

---

Debugging Tips

Add temporary logging before verification:

```javascript console.log('Body type:', typeof req.body); console.log('Signature header:', req.headers['stripe-signature']); console.log('Webhook secret set:', !!process.env.STRIPE_WEBHOOK_SECRET); ```

If signature header is missing entirely, Stripe's test event didn't reach your endpoint at all—check network routing and firewall rules.

---

Related Resources

  • [Stripe API Key Rotation](/?guide=stripe-key-rotation)
  • [Testing Webhooks Locally](/?guide=stripe-webhook-testing)
  • [Official Stripe Webhook Signing Documentation](https://stripe.com/docs/webhooks/signatures)
  • ---

    Found a different variation? Drop it in the comments — especially if you're using a different framework (Django, Rails, Spring) or encountered this with a proxy setup.

    🔥 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