Apps

API keys are visible in your app code

If secret keys for OpenAI, Stripe or other paid services are in your front-end code, anyone can copy them and use them on your bill. Replace the keys and move the calls to a server.

  • Difficulty: Medium
  • Time: 1 to 2 hours
  • Updated October 28, 2026

Symptoms

  • You can see keys in the browser developer tools or page source
  • Your API bill went up for no clear reason
  • Your code calls paid services directly from the browser
  • Secret keys are stored in variables that start with VITE_ or NEXT_PUBLIC_

Why it happens

Code that runs in the browser is public. Anyone can open the developer tools and read it, keys included.

AI tools often put the key right next to the code that uses it, because that makes the demo work. Some keys are made to be public, like a Supabase anon key or a Stripe publishable key. Secret keys are not. People run scripts that scan websites looking for them.

Try this first

  1. Replace the exposed keys first

    Go to each service, create a new key and delete the old one. Do this before anything else. The old key may already be copied.

  2. Move the call to a server function

    Create a small server function that holds the key and talks to the service. Your app calls your function instead of the service. Supabase Edge Functions, Netlify Functions and Next.js route handlers all work. Here is a Next.js example:

    // app/api/chat/route.ts
    export async function POST(req: Request) {
      const { message } = await req.json()
    
      const res = await fetch('https://api.openai.com/v1/chat/completions', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          Authorization: `Bearer ${process.env.OPENAI_API_KEY}`,
        },
        body: JSON.stringify({
          model: 'your-model-name',
          messages: [{ role: 'user', content: message }],
        }),
      })
    
      return Response.json(await res.json())
    }
  3. Store the key without a public prefix

    Call it OPENAI_API_KEY, not VITE_OPENAI_API_KEY or NEXT_PUBLIC_OPENAI_API_KEY. Those prefixes send the value to the browser.

  4. Set spending limits

    Most paid APIs let you set a monthly limit or an alert. Turn it on, so a future leak cannot turn into a huge bill.

  5. Check your git history

    If a key was ever saved in a public repository, treat it as leaked, even if you deleted the file later. The old version is still in the history.

When to stop and call a person

Get help if you are not sure which keys are secret, if many parts of the app call paid services, or if you need limits so one user cannot spend your whole budget. Setting up a proper server layer is a common first job in an app cleanup.

Request a repair crew

Questions people ask

Is my Supabase anon key a problem?

No. It is meant to be public, as long as Row Level Security is turned on for your tables.

Is a Stripe publishable key a problem?

No, that one is public by design. Your Stripe secret key is the one that must stay on the server.

Related fixes