Skip to content

OpenAI API Key Exposed? How to Find, Rotate, and Protect It

A leaked OpenAI key gets scraped by bots within minutes. Here's what to do right now if yours is exposed — and how to make sure it never happens again.

How OpenAI keys get exposed

There are three common ways an OpenAI API key ends up in the wrong hands:

  1. Client-side bundle: The key is set as a NEXT_PUBLIC_OPENAI_API_KEY environment variable, which embeds it in the JavaScript bundle that every visitor downloads
  2. Git commit: The .env file was committed to a public GitHub repo
  3. AI coding tool: Cursor or Copilot suggested hardcoding the key directly in the source file, and it was accepted without review

GitGuardian's 2025 State of Secrets Sprawl report found over 24 million secrets in public GitHub repos. OpenAI keys are among the most common.

Step 1: Check if your key is exposed right now

Open your deployed app in a browser, open DevTools (F12), go to the Network tab, and search for sk- in the response bodies. If you find it, your key is in your bundle.

You can also check directly:

# Download your main JS bundle and search for the key pattern
curl https://your-app.vercel.app | grep -o 'sk-[a-zA-Z0-9]*'

Or check your git history:

git log --all -p | grep 'sk-proj-|sk-[a-zA-Z0-9]{48}'

Step 2: Rotate the key immediately

If your key is exposed, assume it's already been used. Rotate it immediately:

  1. Go to platform.openai.com/api-keys
  2. Click the three dots next to the exposed key → Revoke
  3. Create a new key
  4. Update your environment variables in Vercel/Railway/wherever you deploy
  5. Redeploy

Also check your OpenAI usage dashboard for unexpected charges. If you see usage you don't recognize, contact OpenAI support immediately.

Step 3: Fix the root cause

The OpenAI API should only ever be called from your server. Never from the browser.

Wrong: calling OpenAI from the client

// ❌ This exposes your key in the browser bundle
import OpenAI from 'openai'

const openai = new OpenAI({
  apiKey: process.env.NEXT_PUBLIC_OPENAI_API_KEY, // NEVER do this
  dangerouslyAllowBrowser: true, // The name is a warning
})

Right: server-side API route

// ✅ app/api/chat/route.ts — server only
import OpenAI from 'openai'
import { NextRequest, NextResponse } from 'next/server'

const openai = new OpenAI({
  apiKey: process.env.OPENAI_API_KEY, // No NEXT_PUBLIC_ prefix
})

export async function POST(request: NextRequest) {
  const { message } = await request.json()

  const response = await openai.chat.completions.create({
    model: 'gpt-4o-mini',
    messages: [{ role: 'user', content: message }],
    max_tokens: 500, // Always set this
  })

  return NextResponse.json({
    reply: response.choices[0].message.content
  })
}

Step 4: Add rate limiting

Even with the key on the server, an unprotected endpoint can be abused. Add rate limiting to prevent cost attacks:

import { Ratelimit } from '@upstash/ratelimit'
import { Redis } from '@upstash/redis'

const ratelimit = new Ratelimit({
  redis: Redis.fromEnv(),
  limiter: Ratelimit.slidingWindow(10, '1 m'), // 10 requests per minute
})

export async function POST(request: NextRequest) {
  const ip = request.headers.get('x-forwarded-for') ?? 'unknown'
  const { success } = await ratelimit.limit(ip)

  if (!success) {
    return NextResponse.json(
      { error: 'Too many requests' },
      { status: 429 }
    )
  }

  // ... rest of handler
}

Step 5: Set spending limits

As a last line of defense, set a monthly spending limit in your OpenAI dashboard. Go to Settings → Limits and set a hard limit that matches your expected usage. This caps the damage if your key is ever compromised again.

Scan your app for exposed API keys in 30 seconds.

Start Scanning →

Related articles

OpenAI API Key Exposed: Find & Rotate It Fast | Vezraa