# Your OpenAI or Anthropic key is in the browser. Now what?

By Mark Kashef, founder of Lockpick. Published 2026-10-08, updated 2026-10-08.

What to do in the next ten minutes when an AI provider key is in your frontend code, how to move AI calls to your server, and how to confirm the fix.

Revoke the key now, before you change any code. OpenAI says exposing your API key in client-side environments like browsers or mobile apps allows malicious users to take that key and make requests on your behalf. Anthropic compares an API key to a credit card number, because whoever uses it incurs charges on your behalf.

## What to do right now

1. If you believe your key has been leaked, OpenAI says to rotate it immediately from the API Keys page. If you suspect your Claude API key may be compromised, Anthropic recommends revoking it immediately.
2. Create a new key and store it only in your server settings or your host's secrets.
3. Look at the provider's usage page for requests you did not make.

## How to move the AI call to your server

The browser should call your own route, and your route calls the provider. OpenAI says requests should always be routed through your own backend server, where you can keep your API key secure. A route that forwards anything for anyone is just a new way to spend your credit, so the route below refuses visitors who are not signed in, caps the question, fixes the model on the server and sends back only the answer.

```js
// app/api/answer/route.js on atlas.example, server only
import { getSignedInUser } from '@/lib/auth' // your app's own sign-in check

const MAX_QUESTION_LENGTH = 2000

export async function POST(request) {
  const user = await getSignedInUser(request)
  if (!user) {
    return Response.json({ error: 'Sign in first' }, { status: 401 })
  }

  const { question } = await request.json()
  if (typeof question !== 'string' || question.length > MAX_QUESTION_LENGTH) {
    return Response.json({ error: 'Ask a shorter question' }, { status: 400 })
  }

  const response = await fetch('https://api.openai.com/v1/responses', {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${process.env.OPENAI_API_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ model: process.env.OPENAI_MODEL, input: question }),
  })
  if (!response.ok) {
    return Response.json({ error: 'Try again later' }, { status: 502 })
  }

  const data = await response.json()
  const answer = (data.output ?? [])
    .flatMap(item => item.content ?? [])
    .filter(part => part.type === 'output_text')
    .map(part => part.text)
    .join('')
  return Response.json({ answer })
}
```

The browser sends only the question, and the key never leaves the server. Replace `getSignedInUser` with the sign-in check your app already has. Then add a per-user rate limit, and set a usage limit in your provider's dashboard, so one signed-in account cannot run up your bill either.

## Did it work?

Deploy, then call `/api/answer` while signed out. It should refuse with the sign-in error, not a reply from the model. Then run a [quick check](/check) on the live address. Atlas, our example app at atlas.example, would pass once its scripts no longer carry the key. A clean check does not prove nobody copied the old key, which is why revoking it comes first.

## Questions people ask

### Can I hide the key with obfuscation or a build trick?

No. Anything the browser can use, a visitor can read. Only your server can keep it private.

### Is a server route enough on its own?

It keeps the key private, but the route itself can be abused. Require sign-in or add a rate limit so strangers cannot spend your credit through it.

## Sources

- [API Key Best Practices, Keeping Your Keys Safe and Secure](https://support.claude.com/en/articles/9767949-api-key-best-practices-keeping-your-keys-safe-and-secure), Claude Help Center, read on 2026-10-06
- [Best Practices for API Key Safety](https://help.openai.com/en/articles/5112595-best-practices-for-api-key-safety), OpenAI Help Center, read on 2026-10-06
