Vercel Deployment Security: 10 Misconfigurations That Leak Secrets in 2026
Ten Vercel deployment misconfigurations we find repeatedly during penetration tests. NEXT_PUBLIC_* leaking service keys, preview deployments with production env vars, webhook handlers without signature verification, middleware path-smuggling bypass, unauthenticated API routes fanning out to paid third-party APIs, deployment-URL discovery of stale environments. Each with detection, exploitation, and fix.
Founder of Valtik Studios. Penetration tester. Based in Connecticut, serving US mid-market.
# Vercel deployment security: 10 misconfigurations that leak secrets in 2026
Vercel runs a meaningful chunk of the modern web. Next.js, SvelteKit, Nuxt, Astro, and the long tail of framework deployments all converge on the Vercel edge. The platform is strong out of the box, but the way teams use it creates predictable failure patterns. This post walks through the ten misconfigurations we find most often on Vercel-hosted applications during penetration tests, with the fix for each.
If you run a Vercel deployment and have not done a security review of the project settings in the last year, you almost certainly have at least two of these.
1. NEXT_PUBLIC_* containing actual secrets
The single most common Vercel leak. The NEXT_PUBLIC_ prefix tells Next.js to expose an environment variable to the client bundle. Teams use it for what they think are public values (publishable Stripe keys, Supabase anon keys, Mapbox tokens) and then also use it for things that should stay server-side.
Finding in the wild: NEXT_PUBLIC_SUPABASE_SERVICE_ROLE_KEY embedded in the JS bundle. Service role bypasses RLS. Full database read/write from any browser that loaded the site.
Detection: download the production bundle. grep -E "NEXT_PUBLIC_[A-Z_]+" .next/static/chunks/*.js. Look for anything named SECRET, SERVICE_ROLE, PRIVATE_KEY, API_KEY with admin/write scope.
Fix: rename to drop the NEXT_PUBLIC_ prefix. Reference it only in server components, API routes, or server actions. Rotate the compromised key.
2. Preview deployments with full production environment
Vercel lets you attach environment variables to "Production", "Preview", and "Development" environments separately. Teams that don't split them ship production secrets to every PR branch. Every branch gets its own hostname, publicly accessible, with full access to the production database.
Finding in the wild: https://my-app-git-feature-branch-username.vercel.app serves the PR with the production Stripe live key, the production Supabase service role, and the production OpenAI key. The branch URL is discoverable via GitHub search or predictable subdomain enumeration.
Detection: list all Vercel project environment variables. Check the "Environments" column. Anything marked "Production, Preview, Development" for a sensitive value is the finding.
Fix:
- Set sensitive secrets to "Production" only.
- Provision a separate staging-tier for "Preview" environments (separate Supabase project, separate Stripe test mode, separate S3 bucket).
- Enable "Password Protection" or "Vercel Authentication" on preview deployments via Settings → Deployment Protection.
3. Deployment protection off
Vercel's "Deployment Protection" gates non-production deployments behind Vercel SSO or a shared password. Many teams never enable it. Result: every PR, every feature branch, every abandoned experiment, is a publicly indexable subdomain that search engines (and attackers) can crawl.
Detection: Project → Settings → Deployment Protection. "Standard Protection" should be ON for Preview and Production deployments. If it says "Only Preview Deployments", that's fine. If it says "Disabled", that's the finding.
Fix: enable it. Production can stay public (it's the real site). Preview should require Vercel login or team SSO.
4. Serverless function secrets in git
Happens when someone writes a fallback:
const apiKey = process.env.STRIPE_SECRET_KEY || 'sk_live_abc123...';
Or commits .env.local to the repository "temporarily."
Detection:
gitleaks detect --source . --verbose
trufflehog filesystem .
Fix: rotate every leaked credential. Rewrite git history to scrub the file (git filter-repo or BFG Repo Cleaner). Add pre-commit hooks to block future additions. Use Vercel env vars exclusively.
5. Unauthenticated API routes with no rate limiting
Next.js API routes (/app/api/*/route.ts) run on Vercel Functions by default. They scale automatically, which means an attacker can fan them out to thousands of concurrent requests. If the route talks to a paid third-party API (OpenAI, Anthropic, a paid image-gen provider), you're now paying for the attack.
Finding in the wild: /api/generate-image fronts OpenAI DALL-E with no auth check. Attacker scripts 10,000 image generations. Your OpenAI bill goes from $50/mo to $5,000/mo overnight.
Detection: list every API route. Check which require auth. Check which have rate limits.
Fix:
- Every route: session validation or API key check at the top of the handler
- Rate limit per-IP and per-user using Vercel KV / Redis-based limiter (upstash, ratelimit.dev, or a homegrown token bucket)
- Set hard caps on third-party API spend (OpenAI: usage limits in the platform settings)
6. CORS wide open on API routes
The access-control-allow-origin: * response header with access-control-allow-credentials: true is a broken combo the browser refuses, but * alone allows any origin to POST (and read if not cross-origin-credentialed). This lets third-party sites drain sensitive endpoints.
Finding in the wild: user profile API that returns email + name + address when called cross-origin from anywhere.
Detection:
curl -H "Origin: https://evil.com" -I https://yoursite.com/api/user/me
# look for access-control-allow-origin
Fix: allowlist origins explicitly. For public-read APIs, * is fine only if the route genuinely returns no PII. Otherwise, pin to your own origin.
7. Edge middleware bypass via path encoding
Next.js middleware runs at the edge and is a popular place to put auth checks. It operates on the URL path. Attackers defeat this by path smuggling: %2f encoding, double-encoding, trailing-slash variations, and .. traversal.
Finding in the wild: middleware checks pathname.startsWith('/admin') but allows /%61dmin/users through. The downstream handler normalizes back and serves the admin route.
Detection: test path variants:
/admin(canonical)/Admin(case)/admin/(trailing slash)/%61dmin(encoded)/admin/../admin(traversal)
Fix:
- Use
request.nextUrl.pathname(Next.js decodes once) - Normalize before comparison: lowercase, strip trailing slash, reject encoded segments
- Prefer positive allowlisting over startsWith prefix matching
- Double-check with integration tests specifically for these variants
8. Vercel environment variables exposed via logs
console.log(process.env) or console.log(JSON.stringify(req.headers)) can dump environment secrets into Vercel log streams. Anyone with log read access (potentially a larger group than secret read access) sees them.
Finding in the wild: a debug line left in production prints process.env on every request. Developers with "Member" role on the Vercel team see the full secret set.
Detection:
- Check Function logs for structured dumps
- Grep source for
console.log.*process.env
Fix:
- Remove the debug lines
- Implement structured logging that redacts known secret names (
REDACTEDinstead of the value) - Restrict Vercel team roles: only Owner/Admin should read logs
9. Stale webhook handlers without signature verification
Third-party services (Stripe, GitHub, Slack, SendGrid, Twilio) send webhooks to /api/webhooks/*. Each provider signs requests with an HMAC or JWT. Teams that forget to verify the signature have their webhook endpoints abused to fake events.
Finding in the wild: /api/webhooks/stripe that accepts any POST and processes it as a payment success. Attacker fires fake payment_intent.succeeded events and receives product / credits without paying.
Detection: read each webhook handler. If there's no stripe.webhooks.constructEvent(body, sig, secret) (or equivalent for the provider), it's vulnerable.
Fix: always verify signatures. Set signature secrets via environment. Reject on verification failure before any side effect.
10. Vercel Git integration leak via deployment URLs
Every Vercel deployment gets a URL of the form https://my-app-git-. These URLs are:
- Discoverable (GitHub indexing, Vercel sitemap, subdomain bruteforce)
- Persistent (they stay up until you manually delete old deployments)
- Not covered by
robots.txton the production domain
Finding in the wild: old staging deployments from three years ago, pre-authentication, exposing prototype features that leak database schema, internal admin routes, or product roadmap pages.
Detection:
- Vercel dashboard → Project → Deployments. Sort by oldest.
- Or:
curl -sL https://my-app-git-MASTER-team.vercel.appand enumerate common branch names.
Fix:
- Enable deployment protection (covered in #3)
- Delete stale deployments regularly (Vercel CLI:
vercel rm) - Set retention policy in Team settings
Testing methodology
For a Vercel-hosted target, our checklist:
- Enumerate the main domain + www + common subdomains
- Pull the production JS bundle, grep for
NEXT_PUBLIC_*secrets + hardcoded API keys + auth tokens - Enumerate deployment URLs (
with a branch wordlist)-git- - .vercel.app - Test each API route: auth bypass, CORS, rate limits, input validation, error leakage
- Middleware bypass suite (path encoding, case, trailing slash, traversal)
- Webhook signature verification on every webhook route
- Vercel preview domain discovery via GitHub source
- Public env var exposure via
/api/envor/.envchecks - Source map exposure: if
productionBrowserSourceMaps: true, the full source ships to the client - Vercel.json headers review for missing CSP, HSTS, X-Frame-Options
Our stance
Vercel is a well-built platform. The misconfigurations above are customer-side, not platform-side. That said, sensible defaults could prevent several of them (deployment protection should default ON, PR deployments should not inherit production env vars, etc.). We raise these in our reports and hope the platform evolves toward safer defaults.
Need a Vercel security audit?
If you're running Next.js, SvelteKit, or Nuxt on Vercel and want a focused pentest of the deployment (serverless functions, edge middleware, env vars, webhook handlers, deployment-URL discovery), we offer platform audits specific to this stack.
Contact: hello@valtikstudios.com
Free website security check: /free-check
Sources and further reading
Want us to check your Vercel setup?
Our scanner detects this exact misconfiguration. plus dozens more across 38 platforms. Free website check available, no commitment required.
