Home / Blog / Tutorial
Tutorial·8 min read·July 1, 2026

Exposed API Keys in Your Vibe-Coded App: Find and Fix Them Before They Find You

In February 2026, a developer got an $82,000 cloud bill because one API key leaked. Bots scan GitHub for exposed secrets within minutes of a push. Here is the exact process to find every leaked key in your vibe-coded app and fix it permanently today.

Exposed API Keys in Your Vibe-Coded App: Find and Fix Them Before They Find You

In February 2026, a developer received an $82,000 Google Cloud bill. Their normal monthly spend was $180. One API key, exposed in their frontend bundle, was found by an automated bot and used to run compute jobs they never authorised. The time between the key going live and the bot finding it was measured in minutes, not days. If your app was built with Cursor, Lovable, v0, Bolt, or Replit, exposed API keys in your vibe-coded app are one of the most likely active risks in your codebase right now — and most founders have no idea they are there.

This guide gives you the complete process: how AI tools expose secrets without telling you, where to find every leak in your codebase, how to fix each one permanently, and how to make sure it never happens again.

Why vibe-coded apps leak API keys more than any other type of app

AI coding tools optimise for getting features to work. They put secrets where the code needs them to work — which is wherever the feature is being built. If a feature is in a React component, the API key goes into the React component. If a database call is client-side, the database credentials go client-side. The AI has no concept of "this secret must never reach the browser." It has a concept of "this code must work."

The result: 45% of AI-generated code fails OWASP Top-10 security benchmarks. The most common failure is secret exposure. GitGuardian found over 10 million exposed secrets on public GitHub repositories in one year alone. Security researchers found over 5,000 repositories and 3,000 live production websites leaking ChatGPT API keys through hardcoded source code and client-side JavaScript.

The specific exposure patterns in Cursor, Lovable and Bolt apps:

  • NEXT_PUBLIC_ prefix on secret keys — any environment variable prefixed with NEXT_PUBLIC_ is embedded in the JavaScript bundle and sent to every browser. Most founders do not know this. Many AI-generated Next.js apps prefix database credentials and payment keys with NEXT_PUBLIC_ because it makes them "work" in client-side code.
  • Supabase service_role key in client components — this key bypasses all Row Level Security. If it is in your frontend, any user can read, write, or delete your entire database.
  • .env file committed to a GitHub repository — once pushed, the secrets are in the git history permanently, even after deletion. Bots scrape public repos continuously.
  • Hardcoded keys in component filesconst apiKey = "sk-live-..." directly in JSX or TypeScript.
  • Keys in Vercel build logs — printed via console.log(process.env.STRIPE_SECRET_KEY) during debugging and never removed.

Step 1: Do the two-minute browser check right now

Before reading further, open your deployed app in Chrome, press F12, go to Sources, press Ctrl+F (or Cmd+F on Mac), and search for each of these strings:

sk_live_          (Stripe live secret key)
sk-               (OpenAI API key)
AKIA              (AWS access key)
service_role      (Supabase admin key)
eyJhbGciO         (Any JWT token)

If any of these appear in the source code, you have a critical active exposure. Stop reading and rotate those keys immediately — instructions in Step 3 below.

For a comprehensive automated check of your repo, git history, and live bundle simultaneously, use Mendly's free exposed .env and secret-leak scan. It takes sixty seconds and surfaces every leak pattern in one report.

Step 2: Audit your repository and git history

The browser check only finds what is in the live bundle. Secrets can also be hiding in:

Your .env file in the repository:

# Check if .env is tracked by git
git ls-files | grep .env

# If it appears, it has been committed at some point
# Check if it's in .gitignore
cat .gitignore | grep .env

Your git history:

# Search entire commit history for common secret patterns
git log --all --full-history -p | grep -E "(sk_live|AKIA|sk-|service_role)" | head -50

If your .env was ever committed — even if you deleted it later — the secrets exist permanently in the git history and in every fork or clone of the repository. Rotating the keys is the only fix. Rewriting git history removes the evidence but does not invalidate compromised keys.

Your package.json and build output:

Some build tools embed environment variables into the compiled output. Search the /.next/ or /dist/ folder for the same strings from Step 1.

Step 3: Rotate every exposed key immediately

Do this before fixing the root cause. Assume every key that was exposed has been found. The cost of rotating a key is five minutes. The cost of not rotating a compromised key can be tens of thousands of pounds in fraudulent usage, a complete database breach, or both.

For each exposed key:

  1. Go to the provider dashboard (Stripe, OpenAI, Supabase, AWS, etc.)
  2. Generate a new key
  3. Update the new key in your production environment variables (Vercel, Railway, or wherever you deploy)
  4. Deactivate or delete the old key
  5. Set up billing alerts — most providers offer free spending alerts that will notify you if usage spikes unexpectedly

For a committed .env file:

# Remove from git tracking without deleting the file
git rm --cached .env
git commit -m "Stop tracking .env file"

# Add to .gitignore
echo ".env" >> .gitignore
echo ".env.local" >> .gitignore
git add .gitignore
git commit -m "Add .env to gitignore"

Note: This stops future commits from tracking .env but does not remove it from history. For secrets already in history, rotation is the only meaningful protection.

Step 4: Fix the root cause — move secrets server-side permanently

The rule for Next.js apps:

  • NEXT_PUBLIC_ prefix = embedded in browser bundle = visible to all users. Only use for genuinely public values: analytics IDs, Stripe publishable keys, Google Maps restricted keys.
  • No prefix = server-side only = never reaches the browser. All sensitive keys must have no prefix.
// ❌ WRONG — this key is in every user's browser
const stripe = new Stripe(process.env.NEXT_PUBLIC_STRIPE_SECRET_KEY);

// ✅ CORRECT — this only runs server-side
// In /app/api/payments/route.ts or /pages/api/payments.ts
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY); // No NEXT_PUBLIC_ prefix

For Supabase apps:

The anon key is safe for client-side use — it respects Row Level Security. The service_role key must never leave the server. Check your codebase:

# Find every file that imports or uses the service_role key
grep -r "service_role" ./src
grep -r "SUPABASE_SERVICE_ROLE" ./src

Any result in a file under /app/, /pages/, /components/, or /lib/ (if imported by client components) is a critical exposure. Move those calls to an API route or Edge Function.

For other API keys (OpenAI, Stripe, Razorpay, AWS):

Create a Next.js API route that acts as a proxy:

// /app/api/generate/route.ts — server-side only
import { NextResponse } from 'next/server';

export async function POST(req: Request) {
  const { prompt } = await req.json();

  // OPENAI_API_KEY has no NEXT_PUBLIC_ prefix — stays server-side
  const response = await fetch('https://api.openai.com/v1/chat/completions', {
    headers: {
      'Authorization': `Bearer ${process.env.OPENAI_API_KEY}`,
      'Content-Type': 'application/json',
    },
    method: 'POST',
    body: JSON.stringify({ model: 'gpt-4o', messages: [{ role: 'user', content: prompt }] }),
  });

  const data = await response.json();
  return NextResponse.json(data);
}

Your frontend calls /api/generate — never the OpenAI API directly. The key never leaves your server.

Step 5: Prevent future leaks with three safeguards

Safeguard 1: .env.example file in the repo

# .env.example — commit this, not .env
STRIPE_SECRET_KEY=sk_live_your_key_here
OPENAI_API_KEY=sk-your-key-here
SUPABASE_SERVICE_ROLE_KEY=your-service-role-key-here
NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key-here

This documents what variables the app needs without exposing real values.

Safeguard 2: Validate environment variables on startup

// /lib/env.ts — runs at server startup
const requiredEnvVars = [
  'STRIPE_SECRET_KEY',
  'SUPABASE_SERVICE_ROLE_KEY',
  'OPENAI_API_KEY',
] as const;

for (const envVar of requiredEnvVars) {
  if (!process.env[envVar]) {
    throw new Error(`Missing required environment variable: ${envVar}`);
  }
  // Fail loudly at startup rather than silently at runtime
}

Safeguard 3: Set billing alerts on every provider

Set a spending alert at 2x your typical monthly usage on every cloud provider and API service. This catches both key exposure and runaway AI agent usage. Takes two minutes per provider. Costs nothing.

The DPDP dimension for Indian apps

If your app stores any personal data about Indian users — names, email addresses, phone numbers, payment information — exposed database credentials are not just a financial risk. They are a legal one. India's Digital Personal Data Protection Act, 2023 (DPDP Act) requires that personal data be protected with appropriate technical controls. Fines reach ₹250 crore for serious violations.

Check your current compliance exposure with Mendly's free DPDP compliance check. It audits your access controls, consent capture, and deletion paths against the DPDP Act requirements and gives you a prioritised fix list.

What to do if the scope of the problem is bigger than one afternoon

If the audit in Step 1 and Step 2 surfaces a large number of exposures, or if you are not confident about what you have found, the right move is a professional security audit before you do anything else.

Mendly's free 48-hour audit starts with a complete secret and security review — we check your repo, your git history, your deployed bundle, your Supabase access controls, and your auth layer. You get a prioritised fix list and a flat quote for the remediation work. The Weekend Cleanup sprint at ₹4,999 covers the security layer as the first priority in every engagement.

The Refactor Sprint at ₹14,999 combines security hardening with full structural cleanup — for apps where the exposed secrets are a symptom of broader AI generated code issues rather than a one-off oversight.

For apps built with Cursor, v0, Lovable, Bolt, or Replit, we have a specific audit checklist for each tool's known exposure patterns. The patterns are predictable. So are the fixes.

Scan for exposed secrets free — takes 60 seconds →

Vibe-coded an app that's breaking at scale? We'll audit it free in 48 hours.