/stripe-guides

How to fix Stripe webhook signature error?

Learn how to fix Stripe webhook signature errors by verifying secrets, handling raw payloads, solving body parsing issues, and debugging with Stripe CLI.

Matt Graham, CEO of Rapid Developers

Book a call with an Expert

Starting a new venture? Need to upgrade your web app? RapidDev builds application with your growth in mind.

Book a free consultation

How to fix Stripe webhook signature error?

How to Fix Stripe Webhook Signature Error

 

Step 1: Understand the Stripe Webhook Signature

 

Stripe adds a signature to webhook events to ensure they were sent by Stripe and not by a malicious third party. When you receive a "signature verification failed" error, it means that the signature in the webhook request doesn't match what Stripe expects.

Common causes of this error include:

  • Using the wrong webhook secret
  • Modifying the payload before verification
  • Timing issues (the webhook was received too long after it was sent)
  • Incorrect configuration of your webhook endpoint

 

Step 2: Verify Your Webhook Secret

 

Ensure you're using the correct webhook secret for the specific webhook endpoint.

  1. Log into your Stripe Dashboard
  2. Navigate to Developers > Webhooks
  3. Select the specific webhook endpoint experiencing issues
  4. Click "Reveal" to view the signing secret

Make sure this secret matches what you're using in your code:


// Node.js example
const endpointSecret = 'whsec\_...'; // This should match your Stripe Dashboard

 

Step 3: Properly Handle the Raw Webhook Body

 

A common mistake is modifying the payload before verifying the signature. You must use the raw body string for verification.

For Express.js with Node.js:


const express = require('express');
const stripe = require('stripe')('sk_test_...');
const app = express();

// Important: Use raw body for Stripe signature verification
app.use('/webhook', express.raw({type: 'application/json'}));

app.post('/webhook', (req, res) => {
  const sig = req.headers['stripe-signature'];
  let event;

  try {
    // Verify using the raw body and signature
    event = stripe.webhooks.constructEvent(req.body, sig, endpointSecret);
  } catch (err) {
    console.log(`⚠️ Webhook signature verification failed: ${err.message}`);
    return res.status(400).send(`Webhook Error: ${err.message}`);
  }

  // Handle the event
  console.log('✅ Success:', event.id);
  res.json({received: true});
});

app.listen(3000, () => console.log('Running on port 3000'));

 

Step 4: Solve Body Parsing Issues

 

If you're using a framework that automatically parses JSON bodies, you need to ensure the raw body is preserved.

For Express.js (with middleware order):


const express = require('express');
const app = express();

// IMPORTANT: Route-specific middleware for Stripe webhooks
// must come BEFORE your general body parsers
app.post('/webhook', express.raw({type: 'application/json'}), (req, res) => {
  // Webhook handling code
});

// General body parsing middleware for other routes
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

// Other routes...

 

Step 5: Handle the Payload in Different Frameworks

 

For Django:


# views.py
import stripe
from django.http import HttpResponse
from django.views.decorators.csrf import csrf\_exempt

@csrf\_exempt
def stripe\_webhook(request):
    payload = request.body
    sig_header = request.META['HTTP_STRIPE\_SIGNATURE']
    endpoint_secret = 'whsec_...'  # Your webhook secret
    
    try:
        event = stripe.Webhook.construct\_event(
            payload, sig_header, endpoint_secret
        )
    except ValueError as e:
        # Invalid payload
        return HttpResponse(status=400)
    except stripe.error.SignatureVerificationError as e:
        # Invalid signature
        return HttpResponse(status=400)
    
    # Handle the event
    return HttpResponse(status=200)

 

For Ruby on Rails:


# webhooks\_controller.rb
class WebhooksController < ApplicationController
  skip_before_action :verify_authenticity_token
  
  def create
    payload = request.body.read
    sig_header = request.env['HTTP_STRIPE\_SIGNATURE']
    endpoint_secret = 'whsec_...'
    
    begin
      event = Stripe::Webhook.construct\_event(
        payload, sig_header, endpoint_secret
      )
    rescue JSON::ParserError => e
      # Invalid payload
      render json: {error: e.message}, status: 400
      return
    rescue Stripe::SignatureVerificationError => e
      # Invalid signature
      render json: {error: e.message}, status: 400
      return
    end
    
    # Handle the event
    render json: {status: 'success'}
  end
end

 

Step 6: Check for Timing Issues

 

Stripe has a tolerance window for signature verification (usually 5 minutes). If your server's clock is significantly off, or if there's a long delay in processing, this can cause signature verification to fail.

  1. Make sure your server's time is synchronized (use NTP)
  2. Check for processing delays in your application
  3. Consider increasing the tolerance window:

// Node.js example with custom tolerance
const event = stripe.webhooks.constructEvent(
  payload, 
  signature, 
  endpointSecret, 
  10 \* 60 // 10 minute tolerance window in seconds
);

 

Step 7: Debug Using Stripe CLI

 

The Stripe CLI is an excellent tool for debugging webhook issues:

  1. Install the Stripe CLI from https://stripe.com/docs/stripe-cli
  2. Login with stripe login
  3. Forward events to your local server:

stripe listen --forward-to http://localhost:3000/webhook
  1. In a separate terminal, trigger test webhook events:

stripe trigger payment\_intent.succeeded
  1. The CLI will show both the outgoing webhook and your server's response, making it easier to debug issues.

 

Step 8: Inspect Request Headers and Logs

 

Add detailed logging to capture all incoming webhook information:


app.post('/webhook', express.raw({type: 'application/json'}), (req, res) => {
  console.log('Received webhook:');
  console.log('Headers:', JSON.stringify(req.headers));
  console.log('Body length:', req.body.length);
  console.log('Stripe-Signature:', req.headers['stripe-signature']);
  
  // Continue with signature verification...
});

 

Step 9: Properly Configure Reverse Proxies

 

If you're running behind a proxy (like Nginx, Apache, Cloudflare), it might modify the request body before it reaches your application.

For Nginx, ensure it correctly passes the raw body:


# nginx.conf
location /webhook {
    proxy_pass http://your_backend\_server;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote\_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    
    # Important for webhook body integrity
    proxy_http_version 1.1;
    proxy_request_buffering off;
}

 

Step 10: Use Testing Mode for Webhook Debugging

 

Stripe provides a test mode to help debug issues:

  1. In your Stripe Dashboard, switch to "Test Mode"
  2. Create a new webhook endpoint specifically for testing
  3. Use the test webhook secret in your development environment
  4. Trigger test events using the Stripe CLI or Dashboard

This allows you to debug without affecting your production environment.

 

Conclusion

 

By systematically following these steps, you should be able to identify and fix most Stripe webhook signature errors. Remember that maintaining the raw, unmodified request body is crucial for signature verification. If you continue experiencing issues, Stripe's support team can provide additional assistance by examining your specific webhook logs.

Want to explore opportunities to work with us?

Connect with our team to unlock the full potential of no-code solutions with a no-commitment consultation!

Book a Free Consultation

Client trust and success are our top priorities

When it comes to serving you, we sweat the little things. That’s why our work makes a big impact.

Rapid Dev was an exceptional project management organization and the best development collaborators I've had the pleasure of working with. They do complex work on extremely fast timelines and effectively manage the testing and pre-launch process to deliver the best possible product. I'm extremely impressed with their execution ability.

CPO, Praction - Arkady Sokolov

May 2, 2023

Working with Matt was comparable to having another co-founder on the team, but without the commitment or cost. He has a strategic mindset and willing to change the scope of the project in real time based on the needs of the client. A true strategic thought partner!

Co-Founder, Arc - Donald Muir

Dec 27, 2022

Rapid Dev are 10/10, excellent communicators - the best I've ever encountered in the tech dev space. They always go the extra mile, they genuinely care, they respond quickly, they're flexible, adaptable and their enthusiasm is amazing.

Co-CEO, Grantify - Mat Westergreen-Thorne

Oct 15, 2022

Rapid Dev is an excellent developer for no-code and low-code solutions.
We’ve had great success since launching the platform in November 2023. In a few months, we’ve gained over 1,000 new active users. We’ve also secured several dozen bookings on the platform and seen about 70% new user month-over-month growth since the launch.

Co-Founder, Church Real Estate Marketplace - Emmanuel Brown

May 1, 2024 

Matt’s dedication to executing our vision and his commitment to the project deadline were impressive. 
This was such a specific project, and Matt really delivered. We worked with a really fast turnaround, and he always delivered. The site was a perfect prop for us!

Production Manager, Media Production Company - Samantha Fekete

Sep 23, 2022