"Vercel security" splits into two halves that are best kept apart. The first is what Vercel manages for you and what it lets you configure in the dashboard: deployment protection, per-environment variables, firewall, team roles. The second is what Cursor, v0 or Bolt write into your project and Vercel deploys as is, with its public keys and its unprotected routes. This guide explains Vercel's model, goes through what AI gets wrong when deploying there, leaves you a ready-to-paste vercel.json, a review prompt and four checks from outside. The detail of each flaw is in security when deploying to Vercel.
Part 1: how security works on Vercel
What Vercel does on its own
TLS certificate issued and renewed automatically, HTTP to HTTPS redirect, serverless functions isolated between projects, a global network with DDoS mitigation and a log of every deployment with who made it and from which commit. None of this needs configuring. It also sets a header so search engines do not index previews.
What is your configuration
- Deployment Protection. By default previews are public to anyone with the URL. In Settings you can require a Vercel account from the team (free), a password (paid plans) or restrict by IP. It is the first box to tick.
- Environment variables per environment. Each variable is assigned to Production, Preview or Development. Production keys should not exist in Preview, and sensitive ones are marked Sensitive so they cannot be read back from the dashboard.
- Firewall. Vercel Firewall ships managed rules, a challenge mode against bots and your own rate-limit rules per IP or per route. They are enabled from the dashboard without touching code and are the answer to "someone is burning my OpenAI quota".
- Git and deployments. Every push to any branch deploys. Protect the production branch on GitHub, review which accounts have access to the project and enforce two-factor authentication on the team.
- Tokens and services. CLI tokens, Vercel Blob tokens or those of the integrated databases are server secrets. A team-scoped Vercel token deploys whatever it wants to all your projects.
What depends on the framework, not on Vercel
Which variables reach the browser (NEXT_PUBLIC_, VITE_, NUXT_PUBLIC_), whether source maps are published and which headers are sent is decided by your framework and your configuration, not by the platform. Vercel only runs the build. That is why two apps on Vercel can score an A and an F on headers.
Part 2: what AI gets wrong when deploying to Vercel
- Secret keys with the public prefix because the component "needed them". They travel in the bundle to every visitor. Search for
sk_live,service_roleorAKIAin your chunks. - Test routes deployed.
/api/debug,/api/seedor/api/admincreated for testing and pushed to production without authentication. - Cron without a secret. Vercel calls your cron route with an
Authorizationheader carrying yourCRON_SECRET; if the handler does not check it, anyone triggers the job. - Functions without limits. An endpoint that calls a model or sends emails with no cap per user and no firewall rule.
- Files in public/. A copied
.env, abackup.json, aconfig.yml. Everything in that folder is public. - CORS with wildcard and credentials on the API routes, because "that is how it worked from localhost".
- No vercel.json. No security headers and no redirect from the
.vercel.appdomain to your own, which ends up duplicated on Google.
A ready-to-paste vercel.json
Security headers for every route and a redirect from the Vercel subdomain to your domain. Replace the two names and, if you use Next.js, you can put the headers in next.config instead:
{
"headers": [
{
"source": "/(.*)",
"headers": [
{ "key": "Strict-Transport-Security", "value": "max-age=63072000; includeSubDomains; preload" },
{ "key": "X-Content-Type-Options", "value": "nosniff" },
{ "key": "X-Frame-Options", "value": "DENY" },
{ "key": "Referrer-Policy", "value": "strict-origin-when-cross-origin" },
{ "key": "Permissions-Policy", "value": "camera=(), microphone=(), geolocation=()" }
]
}
],
"redirects": [
{
"source": "/(.*)",
"has": [{ "type": "host", "value": "my-project.vercel.app" }],
"destination": "https://yourdomain.com/$1",
"permanent": true
}
]
}
The Content-Security-Policy goes separately because it depends on your scripts; start in report-only mode as explained in HTTP security headers.
A review prompt before you deploy
Review the security of this project deployed on Vercel and do not change anything yet. Answer with a list:
1. Variables with a public prefix (NEXT_PUBLIC_, VITE_, NUXT_PUBLIC_) containing secret keys.
2. API routes without a session check, test or debug routes, and cron routes that do not verify CRON_SECRET.
3. Endpoints calling paid services without a per-user limit.
4. Files in public/ that should not be public.
5. API routes with Access-Control-Allow-Origin: * and credentials.
6. Whether vercel.json or next.config with security headers exists and whether source maps are disabled in production.
For each point, propose the exact change and wait for my confirmation.
Check the result from the outside
curl -sI https://yourdomain.com | grep -iE "strict-transport|x-frame|x-content-type|content-security"
curl -sI https://my-project.vercel.app | grep -iE "^HTTP|^location"
curl -sI https://my-project-git-main-team.vercel.app | grep -iE "^HTTP"
for p in .env .env.local config.json backup.zip api/debug api/seed; do printf "%-14s " "$p"; curl -s -o /dev/null -w "%{http_code}\n" "https://yourdomain.com/$p"; done
Expected: headers present, a 301 to your domain on the .vercel.app URL, a 401 on the preview URL if protection is on, and 404 on every route in the list. Scanaris runs these checks and about forty more in 30 seconds: it detects that the app is on Vercel and with which framework, looks for secrets in the bundle, checks source maps, headers, CSP, cookies, CORS, TLS and exposed files, and gives you a fix prompt for each failure to paste into Cursor, v0 or Bolt. If your app is Next.js, the Next.js guide explains which code runs on the server and what reaches the browser.
Frequently asked questions
Are Vercel previews private?
Not by default: anyone with the URL opens them, even though they are not indexed. Enable Vercel Authentication under Deployment Protection; it is available on the free plan.
Does Vercel protect my API routes?
No. An API route is a public URL; you check the session in the code. The firewall can rate-limit and stop bots, but it does not decide who is authorised.
Do I need vercel.json if I use Next.js?
Not for headers: next.config has the headers() function. For the redirect from the .vercel.app domain to yours you can use either. What matters is that it exists in one of them.