Valtik Studios
Back to blog
VercelhighUpdated 2026-04-1715 min

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.

Phillip (Tre) Bucchi headshot
Phillip (Tre) Bucchi·Founder, Valtik Studios. Penetration Tester

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 (REDACTED instead 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--.vercel.app. These URLs are:

  • Discoverable (GitHub indexing, Vercel sitemap, subdomain bruteforce)
  • Persistent (they stay up until you manually delete old deployments)
  • Not covered by robots.txt on 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.app and 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:

  1. Enumerate the main domain + www + common subdomains
  2. Pull the production JS bundle, grep for NEXT_PUBLIC_* secrets + hardcoded API keys + auth tokens
  3. Enumerate deployment URLs (-git--.vercel.app with a branch wordlist)
  4. Test each API route: auth bypass, CORS, rate limits, input validation, error leakage
  5. Middleware bypass suite (path encoding, case, trailing slash, traversal)
  6. Webhook signature verification on every webhook route
  7. Vercel preview domain discovery via GitHub source
  8. Public env var exposure via /api/env or /.env checks
  9. Source map exposure: if productionBrowserSourceMaps: true, the full source ships to the client
  10. 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

  1. Vercel: Environment Variables documentation
  2. Vercel: Deployment Protection
  3. Next.js: Middleware limitations
  4. Stripe Webhook Signature Verification
  5. OWASP API Security Top 10
vercelnextjsplatform securityserverlesscloud securitysecret managementwebhook securitypenetration testingresearch

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.

Get new research in your inbox
No spam. No newsletter filler. Only new posts as they publish.