Vercel Deployment Protection returns 401 and blocks a webhook

Stripe or another webhook gets 401 from Vercel before your route runs? Deployment Protection is in front of that URL. A production domain stays public under Standard Protection.

Reviewed October 16, 2026

What it means in plain English

Deployment Protection answers the request before your app does. Vercel says it requires authentication for every request, including Routing Middleware. Stripe, Slack, and GitHub cannot log in, and they cannot set a custom header, so the delivery fails. Vercel's password-protection guide says those webhook calls return 401 when protection is on and the bypass is not configured.

Which URL is locked depends on the scope:

  • Standard Protection protects every domain except production domains. Vercel says that when you enable it, the generated production URL (*.vercel.app) becomes restricted. Your own production domain stays public.
  • All Deployments protects every URL, including the production domain and *.vercel.app.

A webhook pointed at something.vercel.app is behind protection under both of those scopes.

Likely causes

  • The endpoint URL is a *.vercel.app address, including a preview deployment.
  • The project uses All Deployments, so the custom production domain is protected too.
  • Password Protection or Vercel Authentication was turned on without a bypass. Vercel says to enable the bypass in the same step, or CI and webhooks start returning 401.
  • The bypass secret in the webhook URL is an old one. Vercel says regenerating the secret invalidates existing deployments until you redeploy.

How to fix it, step by step

  1. In Vercel, open the project, then Settings, then Deployment Protection. Note the scope: Standard Protection or All Deployments.
  2. If you only need the webhook on the live site, point Stripe at the production domain (https://yourdomain.com/api/...), not at *.vercel.app. Under Standard Protection that production domain is not behind the login. Under All Deployments it still is, so continue to the bypass.
  3. For a URL that stays protected, create Protection Bypass for Automation on that same settings page. Vercel also stores one secret as the VERCEL_AUTOMATION_BYPASS_SECRET environment variable. Team members with at least the member role, or project admins, can create it.
  4. Stripe cannot set headers. Append the secret as a query parameter. Vercel's bypass guide uses this shape:
https://your-deployment.vercel.app/api/webhook?x-vercel-protection-bypass=your-generated-secret

Use your real route and the secret from the dashboard. Vercel notes that the secret is visible in the URL when you do this. Do not commit it in the repo.

  1. If you regenerate the secret, redeploy so the new value is the one the deployment expects.
  2. Keep checking the webhook signature inside your route. The bypass only gets the request past Vercel. It does not prove the caller is Stripe. See Stripe webhook signature verification failed.

How to check it worked

  • Stripe's event delivery for that endpoint is a 2xx, not 401.
  • Opening the bare *.vercel.app URL in a private window still asks you to log in, if Standard Protection or All Deployments is on.
  • The production domain loads for visitors when the scope is Standard Protection.

A blocked webhook is one reason a successful Checkout never upgrades the user. See Stripe paid, but the user was not upgraded.

Sources

More Stripe fixes

Keep going on your own.

The guides stay free. A deep leak report is $15. Fix packs are $24, or $49 for all three. There is no done-for-you repair.

See products