Stripe: webhook signature verification failing [2026 fix]

Webhook signature verification fails when endpoint secret is missing, misconfigured, or timestamp tolerance exceeded; verify secret matches dashboard and check request headers.

Stripe: Webhook Signature Verification Failing [2am Emergency Fix]

TL;DR

Cause: Your endpoint secret doesn't match Stripe's dashboard, or the request timestamp is outside the 5-minute tolerance window. Fix: Grab the correct signing secret from Stripe Dashboard → Developers → Webhooks, and ensure your server clock is synced.

---

Exact Error Messages You're Seeing

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

``` Error: Timestamp outside the tolerance window. ```

``` UnhandledPromiseRejectionWarning: Error: Webhook signature verification failed at Object.constructEvent [as constructEvent] (/node_modules/stripe/lib/utils.js:238:19) ```

``` StripeSignatureVerificationError: No signatures found matching the expected signature for payload. (Webhook Signature Verification Failed) ```

``` warning: stripe signature verification failed, timestamp too old ```

---

The Problem: Side-by-Side Code Comparison

❌ Broken Code

```javascript const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY); const endpointSecret = 'whsec_test1234'; // HARDCODED - WRONG

app.post('/webhook', express.raw({type: 'application/json'}), (req, res) => { const sig = req.headers['stripe-signature']; try { const event = stripe.webhooks.constructEvent( req.body, sig, endpointSecret // Using hardcoded/stale secret ); console.log('✓ Webhook verified'); res.json({received: true}); } catch (err) { console.error('Webhook Error:', err.message); return res.status(400).send(Webhook Error: ${err.message}); } }); ```

✅ Fixed Code

```javascript const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY); const endpointSecret = process.env.STRIPE_WEBHOOK_SECRET; // From env

app.post('/webhook', express.raw({type: 'application/json'}), (req, res) => { const sig = req.headers['stripe-signature']; try { const event = stripe.webhooks.constructEvent( req.body, sig, endpointSecret // Matches dashboard exactly ); console.log('✓ Webhook verified for event:', event.type); res.json({received: true}); } catch (err) { if (err.message.includes('Timestamp')) { console.error('Clock skew detected. Sync server time.'); } else if (err.message.includes('No signatures found')) { console.error('Secret mismatch. Check STRIPE_WEBHOOK_SECRET env var.'); } return res.status(400).send(Webhook Error: ${err.message}); } }); ```

---

Verification Checklist (Do This Now)

1. Confirm Your Endpoint Secret

  • Go to [Stripe Dashboard](https://dashboard.stripe.com/apikeys)
  • Click DevelopersWebhooks
  • Find your endpoint (e.g., example.com/webhook)
  • Click it and copy the Signing secret (starts with whsec_)
  • Verify it matches process.env.STRIPE_WEBHOOK_SECRET in your running process:
  • ```bash echo $STRIPE_WEBHOOK_SECRET # Must show whsec_... ```

    2. Check Server Time Sync

    Stripe rejects requests with timestamps >5 minutes old. If your server clock is behind:

    ```bash

    Linux/Mac

    date # Should match within a minute of https://time.is/

    If wrong, sync:

    sudo ntpdate -s time.nist.gov # Linux sudo sntp -sS time.nist.gov # macOS ```

    3. Verify Request Body Format

    The raw body must be a Buffer, not a parsed JSON object:

    ```javascript // WRONG - body already parsed app.post('/webhook', express.json(), (req, res) => { const event = stripe.webhooks.constructEvent(req.body, sig, secret); // ^ req.body is now an object, signature fails });

    // RIGHT - body stays as Buffer app.post('/webhook', express.raw({type: 'application/json'}), (req, res) => { const event = stripe.webhooks.constructEvent(req.body, sig, secret); // ^ req.body is Buffer, signature works }); ```

    ---

    Still Broken? Check These Too

    1. Environment Variable Not Loaded: Your .env file exists locally but production doesn't have STRIPE_WEBHOOK_SECRET set. Check your deployment platform (Heroku/AWS/etc.) for missing secrets: [Secrets management guide](/?guide=env-variables)

    2. Multiple Endpoints, Wrong Secret: If you have staging and production webhooks, you're using the staging secret on production (or vice versa). Each endpoint has its own signing secret in the Dashboard—double-check the URL matches your environment.

    3. Stripe Package Out of Date: If running stripe@<8.0.0, webhook verification behaves differently. Run npm list stripe and upgrade if necessary: npm install stripe@latest

    4. Request Body Corruption: Reverse proxies (nginx, load balancers) may modify the raw body. Ensure your webhook endpoint is not behind middleware that alters the request before it reaches your handler. [Nginx webhook passthrough](/?guide=nginx-stripe)

    ---

    Quick Deploy Fix (3 Steps)

    ```bash

    1. Grab current secret from dashboard

    STRIPE_WEBHOOK_SECRET='whsec_live_abc123...'

    2. Set in production

    heroku config:set STRIPE_WEBHOOK_SECRET=$STRIPE_WEBHOOK_SECRET

    OR for AWS:

    aws ssm put-parameter --name /stripe/webhook-secret --value whsec_live_abc123

    3. Restart

    heroku dyno restart ```

    ---

    Version Notes

    Stripe Node SDK: This guide applies to stripe@^8.0.0 through current 2026 releases. If using older versions (pre-2020), the constructEvent API differs—check official docs below.

    ---

    Official Resources

  • [Stripe Webhooks Documentation](https://stripe.com/docs/webhooks/signatures)
  • [Node SDK Reference](https://github.com/stripe/stripe-node#webhook-signing)
  • ---

    Found a different variation? Drop it in the comments—help the next person at 2am. 🚀

    🔥 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