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 Developers → Webhooks
  • 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