IS VERCEL SAFE? VERCEL SECURITY REVIEW 2026 | VIBEEVAL
Vercel secures the edge and deploys. Your Next.js app still owns env exposure, route handlers, and middleware — where AI-generated apps usually fail.
SCAN YOUR VERCEL APP NOW
Enter your Vercel URL — we check for leaked env, open API routes, and auth middleware gaps on the live deploy.
Enterprise Infrastructure
Vercel provides robust infrastructure security: automatic HTTPS, a global CDN with DDoS absorption, encrypted secrets at rest, and SOC 2 Type II controls. The platform handles edge termination, certificate rotation, and DDoS scrubbing so that the kinds of attacks that used to require a CDN contract just work. None of that protects you from the part of the attack surface that ships with your code.
The pattern we see most often: teams treat the Vercel “secure by default” marketing as if it covered the application layer. It does not. Every breach we have triaged on a Vercel deployment in the last 18 months has been at the boundary between Vercel and something else — an OAuth scope, a Preview URL leaked to a public PR, an env var that was set on the wrong environment, or a serverless function that trusted its own request headers.
Vercel is safe infrastructure. Vercel is not a free AppSec team. Keep that sentence on a sticky note when AI tools one-click deploy a Next.js app that “just works.”
Security Considerations
Serverless Functions and Edge Runtime
Edge and serverless functions inherit the platform’s hardening, but the request handler is yours. Common failure modes:
- The handler reads
req.headers['x-forwarded-for']and trusts it as the client IP, even though anyone can set that header. Userequest.ip(Edge) or thex-real-ipvalue populated by the platform. - The handler accepts
application/jsonbut does not checkcontent-length, so a 50MB body OOMs the function. - The handler calls a third-party API with a secret pulled from an env var, but the env var is exposed because it’s prefixed with
NEXT_PUBLIC_. - The handler implements auth only on page routes; API route handlers and Server Actions stay open.
The third one is the most common. Anything prefixed NEXT_PUBLIC_ is bundled into the client. We see Stripe live keys, OpenAI keys, and Supabase service role keys shipped to the browser via this exact mistake.
Environment Variables
Vercel scopes env vars per environment (Production, Preview, Development) and per Git branch. Use that. The audits we run keep finding production secrets duplicated into Preview, which means every PR build can talk to the production database.
# Set a secret only on Production, not Preview or Development
vercel env add STRIPE_SECRET_KEY production
# Verify what is exposed where
vercel env ls
For team rotation, never leave service-role keys (Supabase, Firebase Admin SDK, Stripe restricted keys) accessible from Preview. Preview URLs are public unless you enable Deployment Protection, and the env var is fetched at runtime — so a leaked Preview URL is a leaked secret.
When AI tools say “add the env var so the component can fetch,” challenge the architecture: components in the browser should not hold secrets. Move the call to a Route Handler or Server Action that never uses a public prefix.
Preview Deployments
Preview deployments are publicly reachable by default at https://<project>-<hash>-<team>.vercel.app. The hash is not security; it leaks via OG previews, Sentry, GitHub Action logs, and Vercel’s own integrations. Enable Deployment Protection (Vercel Authentication, Password Protection, or Trusted IPs) for any project that touches user data.
// vercel.json — require Vercel SSO on all preview deployments
{
"git": {
"deploymentEnabled": { "main": true }
},
"github": {
"silent": true
}
}
Pair Deployment Protection with noindex headers on Preview environments so that a leaked URL doesn’t end up in Google. The x-robots-tag: noindex header should only ship for non-production deployments — gate it on process.env.VERCEL_ENV !== 'production'.
Application Code
Vercel secures infrastructure, not your code. XSS, broken access control, IDOR, SSRF, and authentication bugs are entirely yours. The platform will happily serve a vulnerable function for as long as you keep it deployed.
The April 2026 Context.ai incident illustrated this cleanly: the platform was not breached. An OAuth integration with broader-than-needed scopes was compromised, and the attacker pulled env vars through the legitimate API. Vercel’s logs showed the access. Nothing was bypassed. The lesson is the one we keep repeating: the integration layer is where things go wrong, not the platform.
Common Misconfigurations We See in Audits
NEXT_PUBLIC_*secrets shipped to browser. Search every repo forNEXT_PUBLIC_and confirm none of them are credentials.- Production env vars set on Preview. Confirm in Settings → Environment Variables that production-only secrets aren’t checked for Preview.
- Edge middleware that doesn’t actually run. A
matcherconfig that excludes the route you wanted to protect. Test the negative path. vercel.jsonredirects that allow open redirects. A redirect with a wildcard destination based on user input is the canonical phishing pivot.- Build logs that print env vars. A
console.log(process.env)left in a build script. Build logs are visible to every team member, and to anyone with the deployment URL on Preview. - Cron routes without secrets. Scheduled jobs publicly invokable.
- Source maps public on production CDN.
- CORS
*on authenticated APIs “to fix local dev.”
Comparison vs Netlify
Vercel and Netlify have similar security postures: both are SOC 2 Type II, both terminate TLS for you, both encrypt secrets, both expose Preview deployments by default. The differences that matter:
- Vercel’s Edge Middleware runs before the function. Netlify’s Edge Functions are similar but bill differently. Both can ship code that bypasses the auth check if the matcher is wrong.
- Netlify’s
_redirectsfile is plaintext and easy to misread; Vercel’svercel.jsonis structured but easier to over-permission with:path*wildcards. - Vercel’s Deployment Protection is the only practical way to keep Preview private without writing your own auth shim. Netlify’s equivalent is Site Password / Identity, which is older and less integrated.
Neither platform will stop a Server Action without auth(). See Is Netlify Safe? for sibling failure modes.
Enterprise Considerations
- SSO: Vercel supports SAML SSO on Enterprise. Without it, every team member is a personal Vercel account that can be phished individually.
- Audit Logs: Available on Pro+ for the team and Enterprise for full event coverage. Pull them into your SIEM; the in-app viewer is not built for incident response.
- SOC 2 boundary: Vercel’s SOC 2 covers the platform. Your application’s data handling is in your scope, not theirs. Auditors will ask. Document the boundary.
- Secret rotation: There is no built-in rotation. Build it into your deploy pipeline or use a secret manager (Doppler, Infisical, AWS Secrets Manager) that pushes to Vercel via API.
- RBAC: Limit who can read production env vars; deploy rights are effectively secret-read rights.
Security Assessment
Strengths
-
- Enterprise-grade infrastructure security
-
- Automatic HTTPS and TLS 1.3
-
- DDoS protection built-in
-
- SOC 2 Type II compliance
-
- Encrypted environment variables with per-environment scoping
-
- Deployment Protection for Preview environments
-
- Edge Middleware for request-time gating
-
- Mature ecosystem for Next.js deploys
Concerns
-
- Application security is developer responsibility
-
- Serverless functions can expose vulnerabilities
-
- Environment variables must be properly scoped per environment
-
- Preview deployments may expose sensitive features when Protection is off
-
NEXT_PUBLIC_prefix is a common secret-leak vector
-
- No native secret rotation
-
- OAuth integrations can read env if over-scoped
AI + Next.js on Vercel: failure gallery
- Server Actions generated without
auth()checks — callable by anyone who knows the action ID. - Route Handlers that trust
x-forwarded-foror skip ownership checks. - Middleware matcher that protects pages but not
/api/*. - Cron routes under
/api/cronwith noCRON_SECRETheader check. - ISR/cache serving personalized HTML to the wrong user.
- v0 UI wired to a Supabase project with RLS still off.
- OpenAI key in
NEXT_PUBLIC_so a client component can “stream tokens.” - Preview sharing production Stripe live keys so “test checkout works.”
These are not Vercel CVEs. They are the predictable residue of generative workflows on a platform that optimizes for shipping.
Hardening checklist
vercel env ls— no production secrets on Preview.- Deployment Protection on for non-public previews.
- No
NEXT_PUBLIC_secrets; grepped in CI. - Middleware + per-handler auth; Server Actions re-check session.
- Headers via
vercel.json/next.config(Security Headers Checker). - Firewall / rate limits on auth and expensive routes.
- OAuth integrations: least scopes; quarterly review (Context.ai lesson).
- Team members audited; audit log enabled on Pro+.
- Live scan production + a sample preview URL.
- Cron/internal routes secret-gated.
- Personalized pages not edge-cached across users.
- Source maps not public on production.
For the full operator guide, see How to Secure Vercel.
How to verify
# Preview should challenge when Protection is on
curl -sI https://<preview>.vercel.app | head
# Bundle should not contain sk_live / service_role
curl -sL https://yoursite.com | wc -c # then use Token Leak Checker UI
Manual: hit /api/admin logged out → 401. Swap resource IDs between two users → 403. Confirm HSTS on the production domain. Confirm preview without Vercel login does not render private product data.
// Server Action pattern reviewers should expect
"use server";
export async function deleteInvoice(id: string) {
const session = await auth();
if (!session) throw new Error("Unauthorized");
const inv = await db.invoice.findUnique({ where: { id } });
if (!inv || inv.ownerId !== session.user.id) throw new Error("Forbidden");
await db.invoice.delete({ where: { id } });
}
OAuth and integration hygiene (post-Context.ai)
- Inventory Team → Integrations quarterly.
- Remove unused apps.
- Prefer integrations that request the minimum scopes.
- After any integration incident in the industry news cycle, rotate env vars that could have been listed via API.
- Treat “deploy bot” tokens like production roots — short-lived, scoped, audited.
Who should use Vercel (security framing)
Good fit: marketing sites, SaaS frontends, AI-generated Next apps with a team willing to own env scoping, middleware, and app authz.
Needs extra care: multi-tenant products with sensitive data (add rigorous RLS/authz testing), regulated workloads (document SOC 2 boundary, consider residency and BAA needs elsewhere in the stack).
Wrong expectation: “We deployed on Vercel so we’re secure.” That sentence is how open Previews and NEXT_PUBLIC_ secrets ship.
The Verdict
Vercel is a safe deployment platform with excellent infrastructure security. SOC 2 compliance and automatic security features make it suitable for production applications. The risk is almost entirely at the boundary you own: env var scoping, Preview protection, OAuth integrations, and the code your serverless functions actually run. Treat the platform as solid and put your effort into the application layer and the integrations you bolt on — especially when AI tools generate the handlers that run at the edge.
Related Resources
How to Secure Vercel
Step-by-step security guide covering Deployment Protection, env var scoping, Edge Middleware patterns, and the OAuth scope review every team should run quarterly.
Vercel Security Checklist
Interactive checklist with launch-blockers, week-one items, and the quarterly review cadence.
Token Leak Checker
Catch NEXT_PUBLIC_ secret mistakes in the shipped bundle.
Is Netlify Safe?
Sibling JAMstack platform failure modes.
Vibe Code Scanner
Live app probing for AI-generated deploys.
Attack paths unique to Vercel + AI apps
- Preview URL leakage — shared in Slack with prod data.
- Middleware false confidence — UI protected, API open.
- Cron without secret — expensive jobs callable by anyone.
NEXT_PUBLIC_mis-prefix — server secrets compiled into the browser.- Edge middleware reading secrets into logs on errors.
Hardening Server Actions and route handlers
import { auth } from "@/auth";
import { z } from "zod";
const Input = z.object({ orderId: z.string().uuid() });
export async function POST(req: Request) {
const session = await auth();
if (!session?.user?.id) return new Response("unauthorized", { status: 401 });
const body = Input.parse(await req.json());
const order = await db.order.findUnique({ where: { id: body.orderId } });
if (!order || order.userId !== session.user.id) {
return new Response("forbidden", { status: 403 });
}
return Response.json(order);
}
Deployment Protection vs application auth
Deployment Protection stops strangers from loading previews. It does not authorize API users on production. Keep both.
Environment matrix that actually works
| Variable | Production | Preview | Development |
|---|---|---|---|
DATABASE_URL |
Prod | Branch DB / ephemeral | Local |
STRIPE_SECRET_KEY |
Live | Test | Test |
SUPABASE_SERVICE_ROLE |
Prod server-only | Never / branch project | Local never committed |
OPENAI_API_KEY |
Prod key + hard cap | Separate low cap | Dev key |
CRON_SECRET |
Unique | Unique | Local |
vercel env ls
vercel env pull .env.local # never commit; ensure Preview lacks prod rows
Middleware matcher pitfalls (Next.js on Vercel)
// Dangerous: protects pages but not API
export const config = { matcher: ["/dashboard/:path*"] };
// Better: include API and exclude only static assets
export const config = {
matcher: ["/((?!_next/static|_next/image|favicon.ico).*)"],
};
After any matcher change, curl /api/* logged-out and as the wrong user. AI “fix login loop” edits often narrow matchers until APIs are naked.
Cron and internal routes
export async function GET(req: Request) {
if (req.headers.get("authorization") !== `Bearer ${process.env.CRON_SECRET}`) {
return new Response("unauthorized", { status: 401 });
}
// job
}
Vercel Cron is not a secret. Anyone who can guess the path can hit it without the header check. Rotate CRON_SECRET if it ever appeared in logs or client code.
ISR / cache personalization bugs
Caching authenticated HTML at the edge serves User A’s dashboard to User B. Rules:
- Personalized pages:
export const dynamic = "force-dynamic"or appropriatecache: 'no-store'. - Never put tenant data in shared static paths without variation keys.
- Review AI-generated
fetchcache options on user-specific data.
Source maps and build logs
Disable public source maps in production or upload only to authenticated error trackers. Ban console.log(process.env) in build scripts—team-visible logs leak. Scan production with Token Leak Checker after each major deploy.
Firewall and rate limits
Use Vercel WAF / firewall rules for auth paths and expensive AI routes; still keep app-layer Upstash limits for multi-instance fairness. Platform DDoS ≠ credential stuffing controls.
Integration audit after Context.ai-class news
- Team → Integrations: remove unused.
- Rotate env that integrations could list.
- Confirm OAuth apps still need current scopes.
- Check deploy hooks and tokens age.
Verification pack for a Vercel SaaS
curl -sI "https://$PREVIEW" | head
curl -s -o /dev/null -w '%{http_code}\n' -X POST "https://$PROD/api/invoices" -H 'content-type: application/json' -d '{}'
Manual: two users swap invoice IDs; expect 403/404. Confirm HSTS on custom domain. Confirm cron without secret returns 401.
Scan Your Vercel App
Let VibeEval scan your Vercel deployment for security vulnerabilities — including the missing-auth, open-redirect, and exposed-env-var patterns that account for most production incidents.
COMMON QUESTIONS
SECURE PAST VERCEL DEFAULTS
Edge TLS is not app security. Probe the live deployment for secrets, open handlers, and access-control failures.
14-day free trial · No credit card · Cancel anytime