Your webhook works locally, but Stripe's deliveries to a *.vercel.app URL fail with a 4xx and your function logs show nothing. Deployment Protection is answering before your code does.
"Deployment Protection requires authentication for all requests" (Vercel docs). With Standard Protection, Vercel "protects all domains except production domains". With All Deployments, it protects "all URLs, including your production domain" (source). Stripe can't sign in to Vercel, and Stripe treats 4xx responses from access-restricted URLs (401, 403) as failed deliveries (Stripe docs).
Under Standard Protection your production domain stays public, so use it, for example https://www.yourapp.com/api/webhooks/stripe, not a preview or deployment URL. Use the exact domain your project serves without a redirect. Vercel can redirect between apex and www (Vercel docs), and Stripe treats redirects as failures (source). If the project uses All Deployments protection, the production domain is protected too, so use Fix 2 or change the protection scope.
Vercel offers "Protection Bypass for Automation" on all plans. For services that can't send custom headers, Vercel says to "append the bypass secret as a query parameter", and gives a Stripe webhook URL as its example (Vercel docs):
https://your-app.vercel.app/api/stripe-webhook?x-vercel-protection-bypass=YOUR_BYPASS_SECRET
VERCEL_AUTOMATION_BYPASS_SECRET (source).x-vercel-set-bypass-cookie for Stripe. Vercel sets that cookie "using a redirect" (source), and Stripe counts redirects as failed deliveries (source).curl -i -X POST "https://your-app.vercel.app/api/webhooks/stripe?x-vercel-protection-bypass=YOUR_BYPASS_SECRET"
# Expect a 400 from YOUR code (unsigned request rejected), not a Vercel login page.
Then resend a failed event from the Stripe Dashboard and check for a 2xx (source).
Paste your webhook URL. WebhookProof sends 3 harmless test requests and tells you whether Stripe can reach it and whether it rejects unsigned events. If it finds a problem, the $9 fix pack gives you the exact copy-paste fix for your stack (refundable for 14 days).
Run the free checkDeployment Protection requires authentication for every request to a protected URL. Stripe cannot authenticate, so the delivery fails before your function runs.
Yes. Vercel's Protection Bypass for Automation lets you append x-vercel-protection-bypass=<secret> as a query parameter to the webhook URL; Vercel documents this for Stripe.
Under Standard Protection, no: production domains stay public. Under All Deployments, yes: every URL, including production, requires authentication.
Stripe webhook not working? Check these 7 things in order · A customer paid but didn't get access. What went wrong? · No signatures found matching the expected signature for payload · Stripe webhook failing with a 307 or 308 redirect
Last reviewed 2026-10-18. WebhookProof is an independent tool, not affiliated with Stripe, Inc.. Quotes are from the linked documentation.