Skip to content
Docs

How do I add password protection to my Vercel deployment?

Enable Password Protection on a Vercel deployment, configure automation and CORS bypasses, and verify the gate before your next CI run.

Vercel

Turning on Password Protection without configuring its bypasses in the same session breaks automation. CI jobs, webhooks from services like Stripe and Slack, and cross-origin browser requests start returning 401 because protection runs at Vercel's edge, before your application code. Enable protection and its bypasses together so those requests keep working.

Password Protection is included on Enterprise, or available on Pro for $20 per protected project per month. It isn't available on Hobby. If you're on Hobby, use the middleware alternative covered at the end of this guide.

This guide walks through what you need, how to turn protection on, how to keep automation and CORS working, how to verify the gate, and when to choose native protection over custom middleware.

Copy link to headingWhat you need to password-protect a Vercel deployment

Check your plan first, because the Password Protection toggle is plan-gated:

PlanPassword ProtectionNotes
HobbyNot availableUse Vercel Authentication or the middleware alternative
ProAvailable per project at $20 per project per monthDisable it to stop future charges for that project
EnterpriseIncluded at the team levelNo per-project charge

Vercel Authentication can protect all deployment URLs, including production domains, at no additional cost on Hobby, Pro, and Enterprise. Deployment Protection Exceptions are also free on every plan. Password Protection has separate plan and pricing requirements.

Next, decide how much to protect. Password Protection supports two scopes:

  • Standard Protection: Covers every deployment except your production custom domain. Available on all plans.
  • All Deployments: Covers every URL, including the production domain and generated vercel.app URLs. Available on Pro and Enterprise.

For an internal tool or a pre-launch site, choose All Deployments. If you only need previews kept private, Standard Protection is enough.

Protection runs at Vercel's edge, before your application code. It intercepts every request, including Routing Middleware, CORS preflights, server-to-server calls, and static file requests, before your route handlers run. The 401 errors covered later all follow from that edge-layer behavior.

Copy link to headingHow to turn on Password Protection in the dashboard

You configure Password Protection in the same Deployment Protection settings where you set the scope. To turn it on:

  1. From the Vercel dashboard, select your project.
  2. Open Settings, then select Deployment Protection.
  3. In the Password Protection section, use the toggle to enable it. Once activated, you will be charged $20 per month for that project.
  4. Select the deployment scope, Standard Protection or All Deployments.
  5. Enter a password.
  6. Select Save.

After you save, existing and future deployments require the password.

A few behaviors apply once protection is active:

  • Cookie persistence: After a visitor enters the password, Vercel sets a cookie for that deployment URL so they aren't prompted again. Changing the password invalidates the cookie.
  • Disabling protection: Turning the feature off makes all previously protected deployments public immediately, with no grace period.
  • Token scope: The JWT set as a cookie is valid only for the URL it was issued on and can't be reused across different deployment URLs, even when they point to the same deployment.

Vercel enables Deployment Protection with Vercel Authentication by default on every new project. Password Protection extends that default to external stakeholders, such as clients and reviewers who don't have Vercel accounts.

Copy link to headingConfigure Password Protection with the API

To manage protection programmatically or with Terraform, update the project's passwordProtection object through the Vercel API. Send the object with your chosen scope and password:

{
"passwordProtection": {
"deploymentType": "prod_deployment_urls_and_all_previews",
"password": "your_password_here"
}
}

The deploymentType field accepts prod_deployment_urls_and_all_previews for Standard Protection, all for All Deployments, and preview for preview deployments only. To disable protection, set the object to null:

{
"passwordProtection": null
}

The dashboard and API produce the same result, so use whichever fits your workflow.

Copy link to headingHow to keep automation working after you password-protect a deployment

Every protection method blocks all requests, including automated ones, so your CI jobs, tests, and webhooks return 401 the moment you turn protection on. Use Protection Bypass for Automation to let those requests through.

Protection Bypass for Automation works on all plans. Vercel injects the secret automatically as the VERCEL_AUTOMATION_BYPASS_SECRET system environment variable, so no setup is required. Send it as the x-vercel-protection-bypass header, or as a query parameter when a tool can't set custom headers. You can create multiple secrets per project, each with a label like CI pipeline or Playwright tests, so revoking one tool's access doesn't affect the others. Regenerating a secret invalidates existing deployments, so redeploy your project before the new value takes effect.

For end-to-end tests with Playwright, add the bypass header to every request:

import { defineConfig } from '@playwright/test';
if (!process.env.VERCEL_AUTOMATION_BYPASS_SECRET) {
throw new Error(
'VERCEL_AUTOMATION_BYPASS_SECRET is required to run tests against protected deployments',
);
}
export default defineConfig({
use: {
baseURL: process.env.VERCEL_PREVIEW_URL,
extraHTTPHeaders: {
'x-vercel-protection-bypass': process.env.VERCEL_AUTOMATION_BYPASS_SECRET,
'x-vercel-set-bypass-cookie': 'true',
},
},
});

The x-vercel-set-bypass-cookie header sets a cookie so in-browser navigation during the test doesn't trigger another challenge.

Cypress uses the same pattern split across its config and test files. Set the base URL and secret in the config:

import { defineConfig } from 'cypress';
export default defineConfig({
e2e: {
baseUrl: process.env.VERCEL_PREVIEW_URL,
},
env: {
BYPASS_SECRET: process.env.VERCEL_AUTOMATION_BYPASS_SECRET,
},
});

Then include the header on the first request in your test:

cy.visit('/', {
headers: {
'x-vercel-protection-bypass': Cypress.env('BYPASS_SECRET'),
'x-vercel-set-bypass-cookie': 'true',
},
});

Both tools now reach the protected deployment on every request.

For service-to-service calls, Trusted Sources is the recommended approach over long-lived secrets. Callers attach a short-lived OIDC token in the x-vercel-trusted-oidc-idp-token header, and Vercel verifies the signature with no shared secret to rotate. Forward the project's Vercel OIDC token in the request:

import { getVercelOidcToken } from '@vercel/oidc';
await fetch('<https://protected-project.vercel.app/api/data>', {
headers: { 'x-vercel-trusted-oidc-idp-token': await getVercelOidcToken() },
});

The same pattern authorizes external providers such as GitHub Actions, which requests an ID token with core.getIDToken() and passes it in the same header. See Trusted Sources for the full configuration.

If you're migrating from Netlify or Cloudflare, their access-control models don't map directly to Vercel's project-level bypass, so plan a custom bypass or a separate test path. Their equivalents are documented in Netlify password protection and Cloudflare Access policies.

Copy link to headingHow to fix CORS and other 401 errors after enabling password protection

Beyond automation, the most common 401 errors after you enable password protection come from cross-origin browser requests, server-side fetches, and external webhooks. These four patterns account for most of them.

Copy link to headingCORS preflight requests return 401

The browser blocks a cross-origin API call when its OPTIONS preflight returns 401 before your CORS handling runs. The edge intercepts every request, including preflights, and the browser can't attach the bypass header to a preflight. Turn on the OPTIONS Allowlist and add the path prefix, for example /api, so preflight requests to that prefix skip protection. Matching is prefix-based, so /api also covers /api/v1/users. After saving, the preflight returns a non-401 response and the real request proceeds.

Copy link to headingServer-side fetch calls return 401

A fetch() inside a Route Handler or Server Component that targets the deployment's absolute URL returns 401, because the server-to-server request reaches the edge without the visitor's authentication cookie. For client-side calls, use a relative path so the cookie is included automatically:

fetch('/some/path');

For server-side calls, forward the incoming request's cookies manually:

const headers = { cookie: <incoming request header cookies> };
fetch('<incoming request origin>/some/path', { headers });

Both approaches carry the authentication cookie, so the request resolves instead of hitting the gate.

Copy link to headingWebhooks from Stripe and Slack are blocked

External services can't carry the authentication cookie, so Stripe webhook deliveries return 401 and Slack event subscription verification fails. For services that let you set request headers, send the bypass secret as the x-vercel-protection-bypass header. For tools that only accept a URL, append it as a query parameter:

https://your-app.vercel.app/api/webhooks/stripe?x-vercel-protection-bypass=your_bypass_secret_here

Secrets in URLs are logged by proxies and CDNs, so prefer the header method whenever the service supports it.

A visitor authenticates on the preview domain, but XHR calls to an API subdomain still return 401, because the authentication cookie is scoped to the exact host instead of the parent domain. Keep API calls on the same origin with relative paths, or co-locate your frontend and API under one project. Co-locating shares a deploy and rollback lifecycle, which is the tradeoff for keeping one origin.

Copy link to headingHow to verify your deployment password protection is working

Run these checks right after saving to confirm the gate is closed:

  1. Test the password prompt. Open the deployment URL in a private browser window. You should see the Vercel password screen, not your app. Entering the correct password sets the cookie and grants access for that deployment URL.
  2. Test the automation bypass. Run curl -H "x-vercel-protection-bypass: your_bypass_secret_here" <https://your-deployment.vercel.app> against the deployment. A 200 confirms the bypass works. A 401 means the request isn't authorized; if you regenerated the secret, redeploy so the deployment uses the new value.
  3. Test a CORS preflight. Send a cross-origin OPTIONS request to a path on your allowlist and confirm a non-401 response. If the 401 persists, check that the allowlist prefix matches the actual path, since matching is prefix-based, not exact.

Confirm the scope covers what you expect. Past production deployment URLs like my-project-main.vercel.app are covered under All Deployments, while legacy Pre-Production Deployments leaves them public. If protecting historical URLs is a requirement, set the scope to All Deployments now.

Copy link to headingWhy native Password Protection covers more than custom middleware

A middleware.ts file with Basic Auth logic is the common alternative to the paid feature. It works for a personal project, but it can't reach the surfaces native protection covers.

Because Password Protection runs at the edge before any application code, it covers generated vercel.app URLs, past production deployment URLs, and static assets that application-layer middleware is blind to. Vercel enables Deployment Protection by default on new projects to close that coverage gap.

This table compares the two approaches:

SurfaceCustom middleware Basic AuthNative Password Protection
Static asset protectionOften skips static asset paths unless the matcher is configured for themProtects every request at the edge, including assets
Shareable Links and access requestsNo platform visibility into custom authWork natively with the protection flow
Generated and past production URLsRequires a matcher covering all pathsConfigured at the project level, covering existing and future deployments per the selected scope

On Hobby, or when you don't want the per-project charge, the middleware path is a documented workaround. Start with the Basic Auth Password Protection template and configure the middleware matcher carefully, since common examples exclude paths such as next/static, next/image, and favicon.ico. It trades edge-level coverage for zero platform cost, which is reasonable for a personal project and risky for staging data.

Copy link to headingNext steps

With protection and its bypasses configured, you're ready to apply this to a project. Start a new Vercel project and enable Deployment Protection in its settings, or browse the templates for a framework-ready starting point.

Copy link to headingFrequently asked questions

Copy link to headingWhy does my CI pipeline return 401 after I enable Password Protection?

Password Protection runs at Vercel's edge and blocks every unauthenticated request, including automated ones, before your application code runs. Configure Protection Bypass for Automation, then send the generated secret as the x-vercel-protection-bypass header or query parameter. The VERCEL_AUTOMATION_BYPASS_SECRET environment variable is set automatically for use in your pipeline.

Copy link to headingDoes Password Protection apply to my custom production domain?

Only under the All Deployments scope, which is available on Pro and Enterprise. The default Standard Protection scope covers preview deployments and generated deployment URLs but leaves your custom production domain publicly accessible. To gate the production domain with a password, select All Deployments when you configure the feature.

Copy link to headingWhat happens to existing deployments if I disable Password Protection?

Every previously protected deployment becomes publicly accessible immediately, with no grace period. Disabling the feature removes the password gate across all existing and future deployments for that project. If you re-enable it later, visitors must enter the password again, and any previous access cookies no longer apply.

Copy link to headingCan I use Password Protection and Vercel Authentication at the same time?

Yes. The two methods are compatible and can run together on the same project. Password Protection covers external stakeholders without Vercel accounts, while Vercel Authentication restricts access to your Vercel team members. On the Enterprise plan, you can layer Trusted IPs on top of both for IP-based restriction.

Related documentation

More Deployment Protection guides