Vercel makes deploying a Next.js app feel effortless. Push to Git, wait a few seconds, and your app is live with a URL anyone can visit. That simplicity is the product. But that simplicity also hides a problem: what you deploy is visible to everyone, including your secrets.
If your Vercel deployment has an API key in the JavaScript bundle, an open /api/ endpoint with no auth, or a misconfigured environment variable, it's already exposed. You just don't know it yet.
A Vercel API leak scanner checks your live deployment for exactly these issues. This guide explains what leaks happen on Vercel, why they're so common in vibe-coded apps, and how to find and fix every one of them.
Why Vercel deployments leak API keys
Vercel itself is secure. The problem is what gets deployed to it. When you build a Next.js app with Cursor, Bolt, or Lovable, the generated code often takes shortcuts that work locally but become security holes in production.
Here's how API keys end up exposed on Vercel:
- The
NEXT_PUBLIC_prefix trap. In Next.js, any environment variable starting withNEXT_PUBLIC_gets embedded in the client-side JavaScript bundle. Vercel builds your app and serves those bundles publicly at/_next/static/. If a secret key has that prefix, it's readable by anyone with a browser. - Hardcoded keys in source code. Code generation tools sometimes write API keys directly into component files instead of using environment variables at all. These get compiled into the bundle regardless of your env config.
- Preview deployments with production secrets. Every pull request on Vercel gets its own deployment. If your preview environment uses the same API keys as production, every PR reviewer (and anyone with the preview URL) has access to your production secrets.
- Build logs exposing variables. If your build script logs environment variables for debugging, those logs might be accessible to anyone with access to the Vercel dashboard or CI output.
What a Vercel API leak scanner finds
A Vercel API leak scanner analyzes your deployed app from the outside, the same way an attacker would. No code access, no repo connection. Just the live URL. Here's what it checks:
1. Secrets in JavaScript bundles
The scanner downloads and analyzes every JavaScript file served by your Vercel deployment. It searches for patterns that match known API key formats:
- Supabase keys:
eyJhbGciOiJIUzI1NiIs...(service role keys that bypass RLS) - Stripe keys:
sk_live_,sk_test_,pk_live_ - OpenAI keys:
sk-followed by 48+ characters - Firebase credentials:
AIzaSyprefixed keys - AWS access keys:
AKIAprefixed strings - Database URLs:
postgresql://,mongodb+srv://connection strings - JWT secrets, webhook signing keys, and any other high-entropy strings that look like credentials
This is the most critical check. A single exposed Supabase service_role key means full database access. An exposed Stripe secret key means access to customer payment data.
2. Unprotected API routes
Vercel serves your Next.js API routes as serverless functions. The scanner probes common endpoints to check if they respond without authentication:
/api/users,/api/admin,/api/data/api/auth/endpoints that might leak session info- CRUD endpoints that accept POST/PUT/DELETE without auth headers
- Webhook receivers that don't verify signatures
Any API route that returns data or accepts mutations without verifying the caller is a vulnerability.
3. Missing security headers
Vercel doesn't add security headers by default. Your app needs to configure them explicitly. The scanner checks for:
- Content-Security-Policy. Without it, your app is vulnerable to XSS. This is the most impactful missing header.
- Strict-Transport-Security. Forces browsers to use HTTPS. Vercel serves HTTPS by default, but without HSTS, the first request could be intercepted.
- X-Frame-Options. Prevents clickjacking by blocking iframe embedding.
- X-Content-Type-Options. Prevents MIME sniffing attacks.
- Referrer-Policy. Controls what URL information is sent when navigating away.
How to add them on Vercel:
// next.config.ts
export default {
async headers() {
return [{
source: '/(.*)',
headers: [
{ key: 'Content-Security-Policy',
value: "default-src 'self'; script-src 'self' 'unsafe-inline';" },
{ key: 'Strict-Transport-Security',
value: 'max-age=63072000; includeSubDomains; preload' },
{ key: 'X-Frame-Options', value: 'DENY' },
{ key: 'X-Content-Type-Options', value: 'nosniff' },
],
}];
},
};4. Exposed configuration files
The scanner probes for files that should never be publicly accessible:
/.envand/.env.local: your environment variables in plain text/.git/config: your Git configuration, potentially exposing repo URLs/vercel.json: your deployment configuration/_next/static/source maps: your original TypeScript source code
Vercel normally handles static file serving correctly, but custom rewrites, misconfigured public/ directories, or accidentally committed files can still expose sensitive data.
5. SSL/TLS configuration
Even on Vercel (which provides automatic HTTPS), the scanner verifies that your certificate is valid, that HTTP redirects to HTTPS, and that no mixed-content issues exist.
Vercel-specific risks most developers miss
Beyond the standard vulnerabilities, Vercel deployments have unique risks:
Preview deployment leaks
Every branch push creates a new deployment at a predictable URL. If your environment variables are shared across preview and production, anyone who finds a preview URL has the same access as your production app. Vercel lets you scope env vars to specific environments (Production, Preview, Development). Use this.
Serverless function cold start information
Vercel serverless functions can leak stack information in error responses during cold starts. If your API route throws an unhandled error, the response might include file paths, dependency versions, or even partial stack traces that help an attacker understand your setup.
Fix: always wrap your API route handlers in try/catch and return generic error messages:
export async function GET() {
try {
// your logic
} catch {
return Response.json(
{ error: 'Internal server error' },
{ status: 500 }
);
}
}The x-vercel-id header
Vercel adds an x-vercel-id header to every response. This is harmless on its own, but combined with other information it confirms your hosting provider and deployment region. Consider adding x-powered-by: false to your config to remove the Next.js identifier at minimum.
How to scan your Vercel deployment
You could manually check each of these: download your JS bundles, search for key patterns, test every API route, inspect every response header. Realistically, that takes hours and you will miss things.
A Vercel API leak scanner automates the entire process:
- Paste your Vercel URL. Your
.vercel.appdomain or custom domain. No code access needed. - Wait 30 seconds. The scanner analyzes your JavaScript bundles, probes your API routes, checks your headers, and tests for exposed files.
- Get your report. Every issue gets a severity rating and a specific fix. Critical issues (exposed keys) are flagged first.
The scan checks 150+ vulnerability patterns specific to Next.js and Vercel deployments. It catches the things that code review misses because it scans the deployed app, not your source code.
How to fix API leaks on Vercel
If the scan finds exposed keys or open endpoints, here's the priority order:
1. Rotate every exposed key immediately
If a key was in your JavaScript bundle, assume it's been harvested. Go to each service dashboard (Supabase, Stripe, OpenAI) and generate new keys. Update them in Vercel's environment variables. Redeploy.
2. Move secrets server-side
Remove the NEXT_PUBLIC_ prefix from any secret key. Access these services only from API routes, server components, or server actions. Client-side code should only use public/anon keys.
// Before (leaked in bundle)
const supabase = createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_SERVICE_ROLE_KEY! // DANGER
);
// After (server-side only)
import 'server-only';
const supabaseAdmin = createClient(
process.env.SUPABASE_URL!,
process.env.SUPABASE_SERVICE_ROLE_KEY! // Safe
);3. Add authentication to API routes
Every API route that reads or writes data should verify the caller. Use middleware for consistent protection:
// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export function middleware(req: NextRequest) {
if (req.nextUrl.pathname.startsWith('/api/')) {
const token = req.headers.get('authorization');
if (!token) {
return NextResponse.json(
{ error: 'Unauthorized' },
{ status: 401 }
);
}
}
}4. Configure security headers
Add the headers config shown above to your next.config.ts. This single change protects against XSS, clickjacking, and protocol downgrade attacks.
5. Scope environment variables
In the Vercel dashboard, set sensitive variables to "Production" only. Use separate, less-privileged keys for preview deployments. Never use production database credentials in preview environments.
6. Re-scan after fixes
After deploying your fixes, run the scanner again. Verify that every critical and high-severity issue is resolved. This takes 30 seconds and confirms your fixes actually work in production.
Vercel deployment security checklist
Run through this before every production deployment:
- No secret keys use the
NEXT_PUBLIC_prefix - Sensitive env vars are scoped to "Production" only
- Preview deployments use separate, limited-privilege keys
- All API routes require authentication
- Security headers configured in
next.config.ts poweredByHeader: falsein your Next.js config- Source maps disabled in production
- API routes wrapped in try/catch with generic error messages
- No hardcoded keys in source code
- All previously exposed keys have been rotated
Or skip the checklist. Run an automated scan and get a complete report in 30 seconds.
Deploy fast, but deploy safe
Vercel removes every friction point between your code and production. That's powerful, but it also means mistakes go live instantly. A secret key committed to a client component is publicly readable within seconds of your push.
A Vercel API leak scanner takes 30 seconds. It costs nothing. And it shows you exactly what's exposed before anyone else finds it.
Your deployment is live. Make sure it's safe.