If you're integrating Stripe in a Node.js backend, you're likely using webhooks to handle events like checkout.session.completed. One of the common pitfalls during deployment is related to webhook signature verification failures, and recently, I ran into exactly this issue.
In this post, I’ll walk through the problem I faced, how I fixed it, and what best practices you can follow to avoid the same mistake.
app.post(
"/api/v1/webhook/stripe",
express.raw({ type: "application/json" }),
stripeWebhookHandler
);
// JSON body parser for all other routes
app.use((req, res, next) => {
if (req.originalUrl === "/api/v1/webhook/stripe") {
next(); // Skip body parsing for Stripe
} else {
express.json()(req, res, next);
}
});
This is required because Stripe needs access to the raw body to verify the signature using the stripe.webhooks.constructEvent() method.
**
❌ The Problem
**
After deploying to production, Stripe kept returning this error:
❗ "Webhook signature verification failed. No signatures found matching the expected signature for payload."
I was sure my raw body handling was correct, and the endpoint URL was accurate. Locally, everything worked using the Stripe CLI. But in production… webhook requests kept failing.
🔍 The Root Cause
Turns out the issue was very simple but easy to overlook:
👉 I was using the Test Mode webhook signing secret (whsec_...) in production, while Stripe was sending Live Mode events.
Stripe signs test and live events with different secrets, and if you mismatch them, signature verification will always fail — even if your code is perfect.
✅ The Fix: Environment-Based Configuration
To avoid this, I updated my environment variables and Stripe initialization code to handle different modes based on the environment
🧪 Bonus Tip: Use Stripe CLI for Local Testing
To test webhooks locally with the Stripe CLI:
stripe login
stripe listen --forward-to localhost:5000/api/v1/webhook/stripe
stripe trigger checkout.session.completed
Make sure your local environment uses the test mode secrets to match the CLI’s default behavior.
💡 Final Thoughts
Small mistakes like using the wrong webhook secret can cost you hours of debugging. If you're getting a "Webhook signature verification failed" error, double-check your mode (test/live) and environment configuration.
If this helped you, share it with someone struggling with Stripe setup — and happy coding! ⚡
Top comments (2)
The Test Mode vs Live Mode secret mismatch is such a common gotcha - I've seen it catch multiple teams. Good callout on keeping environment configs explicit.
One thing that's helped me with webhook debugging: having visibility into the actual request as it arrives, before any middleware processing. When you can see the raw payload and headers hitting your endpoint, it's much easier to verify everything is aligned.
At toran.sh we focus on this kind of API observability - seeing exactly what's coming over the wire. For webhook-heavy workflows (especially payments), that visibility layer has saved me from many similar debugging sessions.
What's your approach for debugging webhooks in production when you can't easily reproduce the exact payload that failed?
THANK YOU SO MUCH! I WAS HAVING THIS ISSUE, I SAW YOUR POST AND IT HELPED ME A LOT!!! THANK YOU THANK YOU THANK YOU