Leaked API keys in Next.js — the most common vibe-coded mistake
Every week founders tweet about their OpenAI bill going from $10 to $10,000 overnight. Usually the cause is an API key committed to a public repo. Here is why it happens in Next.js specifically and how to stop it in five minutes.
A leaked Stripe or OpenAI key costs founders real money — usually thousands of dollars before they notice. This guide covers the three places leaks actually happen in Next.js apps, and the controls that stop them.
What it is
API keys grant access to paid services. When one is committed to a public GitHub repository or exposed client-side in a Next.js bundle, automated scrapers pick it up within minutes and start issuing requests. The costs accumulate in real time.
Vulnerable example
// WRONG: this variable ships to the browser because of the NEXT_PUBLIC_ prefix
// every user can open DevTools and read it.
const openai = new OpenAI({
apiKey: process.env.NEXT_PUBLIC_OPENAI_KEY,
});
// WRONG: key hard-coded in a server action that gets committed
export async function chatAction(msg: string) {
const key = "sk-live-..."; // <-- committed to git
...
}Fixed example
// Server-side only. No NEXT_PUBLIC_ prefix; read via process.env at runtime.
import { OpenAI } from "openai";
const openai = new OpenAI({
apiKey: process.env.OPENAI_KEY!, // must never be NEXT_PUBLIC_*
});
// Plus: in .gitignore
// .env.local
// .env.production.localHow Securie catches it
Securie's secret-scanning agent runs on every commit. It detects keys by entropy + context, then live-validates the key against the vendor's API to confirm it is real. When confirmed, the key is reported as critical, an auto-rotation PR is opened, and a bill-shock firewall caps spend on the affected key until rotation completes.
Checklist
- Never prefix a secret with `NEXT_PUBLIC_` — that ships it to the browser
- Use a secrets manager in production (Vercel env vars, 1Password, Doppler, AWS Secrets Manager)
- Enable push-protection on your GitHub org to block commits containing patterns like `sk-live-*`
- Rotate any key that has ever been committed, even if you forced-pushed to delete it — it is in reflog and any clone
- Set a per-day spend cap in your API provider dashboard
FAQ
My key was committed for five minutes then deleted. Am I safe?
No. Automated scrapers index every commit within seconds. Assume the key is compromised. Rotate it.
Does `.env.local` keep keys safe?
It keeps keys out of the bundle, but only if `.env.local` is in `.gitignore` and has never been committed. Check `git log .env.local` to be sure.