stripe listen works perfectly in dev. Production returns errors or silently drops events. The gap between local and production Stripe webhooks is almost always one of four things — and you can check all of them in under 10 minutes.
Stripe webhooks working locally but failing in production in 2026 almost always come down to one of four environment differences: the signing secret in production is different from the one in your local .env file, your production deployment has a middleware or proxy that modifies the raw request body before it reaches the webhook handler (breaking signature verification), the production endpoint URL does not match what is registered in the Stripe Dashboard, or your production server has a timeout shorter than Stripe's 30-second delivery window.
The most common culprit: a reverse proxy (Nginx, Cloudflare, AWS API Gateway) that parses and re-serializes the JSON request body. Stripe's signature is computed over the exact raw bytes of the body — any transformation (even adding/removing whitespace) invalidates the signature. Your webhook handler must receive the raw, unmodified request body. In Express.js, this means using `express.raw({type: 'application/json'})` instead of `express.json()` on the webhook route specifically.
Local Express servers often have consistent middleware ordering. Serverless environments (Vercel, Netlify, AWS Lambda) may apply different middleware or parsing depending on the runtime. Explicitly configure raw body parsing in your production handler — don't rely on framework defaults.
Stripe only sends webhooks to HTTPS endpoints in production. Your local stripe listen tunnel provides HTTPS. If your production URL is HTTP (non-SSL), Stripe rejects the endpoint registration entirely. Ensure your production domain has a valid SSL certificate.
Your Stripe Dashboard webhook endpoint configuration specifies which events to send. If you registered the endpoint with only payment_intent.created but your code handles payment_intent.succeeded, production events never arrive. Check the endpoint's event selection and add all required event types.
Real operator. No ticket queue. San Diego-based. Most issues resolved in one thread.
Related problems in this cluster:
Related guides