← Blog

How to Prevent API Key Leaks in Frontend Apps

2026-09-25 · 5 min read · SubToAPI Team

If you're asking how to prevent API key leaks in frontend code, the short answer is: you can't keep a secret in JavaScript that ships to the browser. Any key, token, or credential bundled into client-side code — React, Vue, a static site, a mobile webview — is visible to anyone who opens dev tools, views page source, or intercepts network traffic. Minifying it, obfuscating it, or storing it in an environment variable prefixed with REACT_APP_ or NEXT_PUBLIC_ doesn't hide it; it just makes it slightly harder to find at a glance.

The real fix isn't a clever hiding technique. It's architectural: never let the browser hold a credential that has direct access to a paid or sensitive API. Instead, put a server between the frontend and the third-party API, and let the frontend talk to your server with a scoped, revocable token instead. Below is a breakdown of why leaks happen, how they actually get exploited, and the patterns that stop them.

Why Frontend API Keys Leak

Every request your browser makes is visible in the Network tab. Every bundled JS file is downloadable and readable. This means:

Once a key leaks, anyone can use it — running up your bill, exhausting rate limits, or accessing data you didn't intend to expose.

The Core Fix: Never Ship Real Credentials to the Client

The only reliable pattern is to move the credential server-side and give the browser something disposable instead.

1. Use a backend proxy

Route all calls to the sensitive API through your own server. The browser calls your API; your server holds the real key and forwards the request.

// server.js — the real key never leaves the server
app.post("/api/chat", async (req, res) => {
  const response = await fetch("https://api.example.com/v1/chat", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.PROVIDER_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify(req.body),
  });
  const data = await response.json();
  res.json(data);
});

The frontend never sees PROVIDER_API_KEY. It only talks to /api/chat, which you fully control.

2. Issue short-lived, scoped tokens instead of raw keys

If you need the browser to authenticate directly (for streaming, for example), don't hand out the provider's master key. Issue a short-lived token scoped to a single user and a narrow set of actions, generated by your backend after it verifies the request.

3. Use per-application keys, not one shared master key

If your provider supports it, generate a distinct API key per application or per environment instead of reusing one key everywhere. That way, if a key does leak, you can revoke just that one without breaking every integration. This is exactly the model SubToAPI uses: instead of exposing your raw Claude access, you generate application keys (sub_live_...) scoped per app, rotate or revoke them individually from the dashboard, and route requests through https://api.subtoapi.app/v1/messages from your backend. Your actual Claude account credentials never touch the frontend at all — see the quickstart for the setup.

curl https://api.subtoapi.app/v1/messages \
  -H "Authorization: Bearer $SUBTOAPI_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "claude-3-5-sonnet",
    "max_tokens": 512,
    "messages": [{"role": "user", "content": "Summarize this ticket"}]
  }'

That call belongs on your server, called from a route your frontend hits — not embedded in client JS.

4. Set spending and rate limits at the key level

Even with a proxy in place, defense in depth matters. Cap usage per key so a compromised token (server-side or otherwise) has a low ceiling. Combine this with request logging so you notice anomalies — a spike in calls from one key or IP is often the first sign of a leak, not the last.

5. Rotate and revoke on a schedule, not just after an incident

Treat key rotation as routine maintenance, not emergency response. If a key was ever pasted into a Slack message, a .env committed by mistake, or a support ticket, rotate it immediately — don't wait to see if it gets abused.

A Practical Checklist

If you're building on top of Claude specifically, pairing this checklist with per-app keys and a hosted proxy (like the one described in the streaming docs or tool use docs) removes most of the manual plumbing — you get the scoping and revocation without writing the proxy yourself.

Frequently asked questions

Can I just hide the API key with obfuscation or Base64 encoding? No. Obfuscation and encoding are reversible in seconds by anyone with dev tools open. They don't count as security — only removing the key from client code entirely does.

Is it safe to use environment variables in a React or Next.js app? Only for non-secret configuration like public URLs or feature flags. Any env var exposed to the client (NEXT_PUBLIC_, REACT_APP_, VITE_*) is bundled into the JS and publicly visible — it's not a safe place for API keys.

What should I do if a key has already leaked? Revoke or rotate it immediately, check usage logs for abnormal activity during the exposure window, and move the integration behind a backend proxy so the new key never reaches the client again.

Turn your Claude access into an HTTPS API

SubToAPI gives you application API keys, streaming, tool use and usage insights on top of your existing Claude access — set up in minutes.

Start free  Read the quickstart →