Your webhook answers with a 307 or 308 redirect
Short answer: A redirect means "go to a different address." Stripe does not follow it. Stripe's docs say it treats any 3xx reply to a webhook as a failure. The fix is to put the final address, the one that doesn't redirect, into your Stripe settings.
Signs you have this problem
- Stripe's Event deliveries shows 301, 302, 307 or 308.
- Your webhook code never runs. There are no logs for it.
- Opening the address in a browser lands you on a slightly different address.
Step 1: Find where it redirects to
curl -i -X POST https://yourapp.com/api/webhook
Read the first line and the location: header:
HTTP/2 308
location: https://www.yourapp.com/api/webhook
That location: address is what Stripe should use. Run curl again on it. Keep going until you get a reply that is not 3xx. (A 400 from your own code is fine here. The test has no signature.)
Step 2: Put the final address in Stripe
In Stripe, open Developers → Webhooks, edit the endpoint, and paste the final address exactly. Match:
https(nothttp)www.or nowww., whichever your host keeps- the trailing slash, or no trailing slash
Common causes
Trailing slash in Next.js. By default, Next.js redirects /api/webhook/ to /api/webhook. If you set trailingSlash: true in next.config.js, it does the opposite. Make the Stripe address match your setting.
www vs no www. Many hosts send one version of your domain to the other. For example, yourapp.com may redirect to www.yourapp.com. Use the one that stays put.
http to https. Hosts often send http:// to https://. Always use https://.
Redirects you wrote. In Next.js, a redirect in next.config.js with permanent: true sends a 308, and permanent: false sends a 307. Make sure none of your redirects match the webhook path.
Login middleware. If your middleware sends logged-out visitors to /login, Stripe gets that redirect too. Let the webhook path skip it.
Step 3: Send a test event
Send a test event from the Stripe Dashboard, or click Resend on a failed one. You want a 2xx. Stripe retries failed live events for up to three days, so old events may come through on their own once the address is fixed.
Check it with a free scan
Paste your webhook address into the free Tillping scan: https://tillping.online/#scan. No account is needed. Only scan an address you own or are allowed to test.
If your address answers with a 301, 302, 307 or 308, the scan tells you the code and follows the redirect (up to 5 steps) to show where the test ended and what that address answered. Use that final address in Stripe, then scan it again to check it answers directly. The scan also flags an address that starts with http://, a bad certificate, and a slow reply.
The curl test above is still the best way to see every step of the chain.
Sources
- Stripe, Webhooks (status code table: 3xx counts as a failure): https://docs.stripe.com/webhooks
- Next.js,
trailingSlash: https://nextjs.org/docs/app/api-reference/config/next-config-js/trailingSlash - Next.js,
redirects(307 vs 308): https://nextjs.org/docs/app/api-reference/config/next-config-js/redirects
Check your webhook now
Paste your webhook address to see what it answers. Free, no account, no keys.
Check my webhookGet an email when Deep Scans open
One email to confirm, then one when we launch. Leave any time.
