Guides / Vercel preview deployments

last checked oct 2026

Are your Vercel preview deployments public? How to check and lock them down

Every branch you push to Vercel gets its own URL, like my-app-git-new-checkout-yourname.vercel.app. Those previews run your unfinished code, often against the same database and API keys as production, and the URLs end up in pull requests, chat threads and screenshots. Whether a stranger can open one comes down to one project setting. Here's how to check it in a minute and turn it on.

The short answer

A preview deployment is only private if Deployment Protection is on for the project. New Vercel projects start with Vercel Authentication at Standard Protection, which puts a Vercel login in front of every deployment except your production domains. Older projects, projects where someone switched it off, and projects in a team whose default was set to None can have fully public previews. To check and fix it:

"noindex" isn't protection. Vercel adds an X-Robots-Tag: noindex header to preview deployments so search engines skip them. That only asks crawlers not to list the page. Anyone who has the URL can still open it.

Why a public preview matters

A preview is a real, running copy of your app built from a branch that isn't finished. Vercel applies the environment variables you marked for Preview to every non-production branch, and in a lot of AI-built projects those are the same live Supabase, Stripe and OpenAI keys production uses. So a public preview can mean:

Production gets your attention before launch. Previews usually don't.

How to check

1. Find your preview URLs

Every deployment is listed on the project's Deployments page. With the Vercel CLI, from your linked project folder:

vercel ls

Pick a recent preview URL, plus the generated URL of your current production deployment (the one ending in a random string, like my-app-abc123xyz.vercel.app, not your custom domain).

2. Ask for it the way a stranger would

This sends no cookies and prints only the status code and where it redirects, if anywhere:

curl -s -o /dev/null -w '%{http_code} %{redirect_url}\n' \
  "https://my-app-git-new-checkout-yourname.vercel.app/"

A 200 with no redirect means your app served the page to an anonymous visitor, so the preview is public. A 401, or a redirect to vercel.com, means a login is in the way. A redirect to your own app's sign-in page doesn't count: that's your app's auth, not Vercel's, and your API routes are still reachable. Run the same check on the generated production URL.

Also see whether the noindex header is there, since a missing one tells you the preview is served on a custom domain (more on that below):

curl -sI "https://my-app-git-new-checkout-yourname.vercel.app/" | grep -i '^x-robots-tag'

3. Read the setting from the API

If you have a Vercel access token in VERCEL_TOKEN, this prints only the protection settings for the project, not your environment variables or the token. Add ?teamId=... if the project belongs to a team:

curl -s -H "Authorization: Bearer $VERCEL_TOKEN" \
  "https://api.vercel.com/v9/projects/my-app" \
  | jq '{ssoProtection, passwordProtection, trustedIps: (.trustedIps.deploymentType // null)}'

"ssoProtection": null means Vercel Authentication is off. A deploymentType of prod_deployment_urls_and_all_previews is Standard Protection, all covers production too, and preview protects previews only.

4. See which keys your previews get

This lists variable names and the environments they apply to. It doesn't print values:

vercel env ls preview

Look for live payment keys, your production database URL, and secret keys you'd never want an unfinished branch to use.

How to fix it

Turn on Vercel Authentication

In the dashboard, open the project, go to Settings → Deployment Protection, switch on Vercel Authentication, choose Standard Protection and save. It applies to existing and future deployments in that scope. Visitors who aren't signed in to Vercel with access to the project get a login page instead of your app.

Or set it with the API, using the same token:

curl -s -X PATCH -H "Authorization: Bearer $VERCEL_TOKEN" \
  -H "Content-Type: application/json" \
  "https://api.vercel.com/v9/projects/my-app" \
  -d '{"ssoProtection":{"deploymentType":"prod_deployment_urls_and_all_previews"}}' \
  | jq '.ssoProtection'

If you run a team with several projects, also set the team default for new projects in your team settings, so the next project doesn't start out public.

Check your VERCEL_URL fetches. With Standard Protection, the generated production deployment URL needs a login too. If your app calls itself through VERCEL_URL or VERCEL_BRANCH_URL (in Next.js, NEXT_PUBLIC_VERCEL_URL), those calls will start failing. Vercel's fix is to call relative paths like fetch('/api/thing') from the browser, and from the server to use the origin of the incoming request.

Give Preview its own keys

Protection decides who can open a preview. Separate keys decide what a preview can damage. In Settings → Environment Variables, set each sensitive variable's Preview value to a test or staging one: Stripe test-mode keys, a separate Supabase project or branch, and a separate, spend-capped key for any AI API. Vercel lets you override a Preview variable for one specific branch, so you only have to change the values that differ. Changes apply to new deployments only, so redeploy the previews you care about.

Clean up who already has a way in

Custom domains on preview branches

If you've put a domain like staging.example.com on a branch, Vercel stops adding the noindex header there, so search engines can list it. Run the curl check above on that domain too, and if it loads without a login, protect it before it gets indexed.

Want production private as well?

Choose All Deployments instead of Standard Protection. It's available on every plan and covers your production domain, which suits an internal tool or a beta you aren't ready to show. Password Protection is a different method: it isn't available on Hobby, and on Pro it's a paid add-on per protected project, so Vercel Authentication is the free route.

Confirm it worked

Re-run the curl check on the preview and the generated production URL from a logged-out terminal. You should no longer get a 200 from your app. Then open your real production domain and make sure it still loads for everyone, unless you chose All Deployments.

next step

Check your live app for free

Our free Leak Checker looks at your deployed app the way any visitor's browser does and flags exposed secret keys, public source maps and missing security headers. Point it at your production domain and at a preview URL you've just checked, and compare what each one gives away.

If you want the deploy settings, keys and database checked together, the $99 Scan + Report covers your repo and live app, including deploy settings, leaked keys and database access rules.

Sources

Every product step above was checked against these official pages. Last checked Oct 2026. Products change; if a setting moved, the docs below are the source of truth.