Stripe Webhook Failing: Quick Fix for Server Admins
Stripe webhooks failing? Usually a server timeout or SSL issue. Here's how to fix it fast without the fluff.
You're Not Alone—This Is a Server Issue, Not Stripe's Fault
I get it. You set up a Stripe webhook, tested it once, it worked. Then two days later, payments are going through but nothing's hitting your server. Emails not sending, subscriptions not updating. Frustrating. But 9 times out of 10, it's your server timing out or SSL acting up. Let's get it fixed in less than ten minutes.
The Fix: Increase Your Server Timeout
Stripe sends webhooks with a 5-second timeout. If your server doesn't respond in that window, Stripe marks it as a failure and retries up to 3 times over 24 hours. But if your server takes 6 seconds to process, it'll fail every time. Here's how to bump that timeout.
For Apache (mod_php or PHP-FPM)
Edit your php.ini file. Find max_execution_time and set it to 30. Also check max_input_time and set it to 30. Save and restart Apache.
max_execution_time = 30
max_input_time = 30
For Nginx with PHP-FPM
Edit your php.ini same as above. Then check /etc/php/8.x/fpm/pool.d/www.conf and add or uncomment:
request_terminate_timeout = 30s
Restart PHP-FPM: sudo systemctl restart php8.3-fpm
For Node.js (Express)
If you're using Express, add a timeout middleware. Here's a quick fix:
const timeout = require('connect-timeout');
app.use(timeout('30s'));
app.use((req, res, next) => {
if (!req.timedout) next();
});
Also Check Your SSL Certificate
Last month I had a client whose Stripe webhook kept failing. Turned out their SSL cert expired that morning. Stripe's webhook infrastructure won't send to a server with a bad cert. Run this command to check yours:
echo | openssl s_client -servername yourdomain.com -connect yourdomain.com:443 2>/dev/null | openssl x509 -noout -dates
If it's expired, renew it through your hosting provider or Let's Encrypt.
Why This Happens
Stripe's webhook system is built for reliability. Their servers wait exactly 5 seconds for your server to respond with a 200 OK. If your code does a database query, sends an email, and calls an external API all in one webhook handler, that can easily chew up 4-5 seconds. Add network latency, and boom—timeout. The fix is simple: either process the webhook async (queue it) or increase the timeout to give yourself breathing room.
Less Common Variations
Sometimes it's not timeout. Here are other sneaky causes I've seen:
- Signature verification failing: Your code checks the webhook signature but the key is wrong. Regenerate the signing secret in Stripe Dashboard and update your code.
- IP whitelist blocking: If your server has a firewall, Stripe's IPs change. Add their full range:
https://stripe.com/docs/ips. I've seen clients block them by accident. - HTTP redirect loop: If your webhook endpoint URL redirects (301/302), Stripe won't follow it. Must be a direct 200 response.
- Response body too large: If your handler returns a massive JSON payload, Stripe might drop it. Keep your response small—just
{"status": "ok"}.
How to Test Without Breaking Production
Use Stripe CLI to test locally. Install it, then run:
stripe listen --forward-to localhost:3000/webhook
Then trigger test events: stripe trigger payment_intent.succeeded. Watch your logs. If it fails locally, it'll fail on production.
Preventing This Long-Term
Don't process heavy tasks inside the webhook handler. Instead, push the event to a queue (Redis, RabbitMQ, or even a simple database table) and process it in a background job. That way your webhook endpoint just returns 200 instantly. Stripe loves that. Also set up monitoring: use UptimeRobot or Pingdom to check your endpoint every 5 minutes. And set up Stripe webhook logs alerts in the Dashboard—they'll email you when failures spike.
One more thing: if you're using a shared hosting plan, their default timeout is often 30 seconds, which is fine. But if you're on a VPS, the default PHP timeout can be 5 seconds. That's the same as Stripe's timeout—recipe for failure. Bump it to 30 and you're golden.
Had a client last month whose entire Stripe integration died because their webhook handler was sending a thank-you email via SMTP. That call took 8 seconds. After moving the email to a queue, webhooks worked instantly.
Was this solution helpful?