Exposed LLM API key in public client-side code: What It Means and How to Fix It

A credential shaped like an OpenAI, Anthropic, Gemini, Groq, Hugging Face or other LLM-provider API key is present in HTML, JavaScript or a source map that anyone can download.

Do this: Revoke the key, then remove every public copy. Deleting the code alone leaves a copied credential usable, while rotating alone leaves the replacement vulnerable to the same build mistake.
PassNo recognisable LLM API key was found in the entered page, linked same-origin client resources or common exposure paths examined.
CriticalA credential-like LLM API key is visible in public client-side code; revoke it before removing the public copy.

The fix, in one snippet

Example to adapt Server-side proxy; the browser receives no provider key
POST /api/chat -> your server -> LLM provider
LLM_API_KEY=stored-in-server-secret-manager

Illustrative values. Change the paths, hostnames and options to match your own site before using it.

The sections below explain what is tested, why it matters and the alternatives.

What we test

Scan.now fetches the page you entered, follows its same-origin JavaScript, explicitly referenced JavaScript or JSON chunks and linked source maps, then requests a fixed list of common .env, Git, backup, SQL/configuration and runtime-config paths. Exposure responses are verified by content so a custom 200 error page is not treated as a real file. Paid owners may provide a transient Authorization or Cookie header for the exact private URL. It is stripped before cross-origin redirects and never stored. The scanner looks for documented provider prefixes and for long credential values assigned to provider-specific names such as OPENAI_API_KEY or MISTRAL_API_KEY. Every request is a read-only GET: JavaScript is not executed, forms are not submitted and discovered values are not sent to providers. A match is redacted in memory before the result is built, so the raw credential is never written to a report or log. Prefix matches are high confidence; opaque values found only beside a provider label are label matches that should be confirmed in your own source. The Premium local-folder audit runs inside the visitor's browser without uploading files; its optional Python companion covers remote-server files and Git patch history that no website scanner can inspect.

Why it matters

An API key in a browser bundle is public even when the repository is private. A visitor can open developer tools, download the JavaScript file, inspect a source map or search the response body without bypassing any control. If the key authorises paid inference, anyone who copies it can run requests against your account, spend the quota, access provider features granted to the key and get the account suspended for traffic you did not send. Provider-side domain restrictions help where they are available, but they are not a substitute for keeping a bearer credential on the server: request headers can be reproduced outside a browser and not every LLM provider offers an enforceable origin restriction. Minification, Base64 encoding and splitting a string across variables only obscure the value; the browser must reconstruct it to use it. The wider exposed LLM API keys guide explains how secrets reach frontend builds and how to design the boundary correctly.

How to fix it

Revoke or rotate the exposed key first. Removing it from the current JavaScript bundle does not invalidate copies already downloaded, cached, committed or archived. Review the provider's usage logs, billing and key list for activity you do not recognise, then create a replacement with the smallest permissions and budget the application needs. Move all calls that require the credential behind a server-side endpoint:

browser  --POST /api/chat-->  your server  --Authorization: Bearer $LLM_API_KEY-->  provider

Store the replacement in the hosting platform's server-only secret manager, not in a variable exposed to the frontend build. Names such as NEXT_PUBLIC_*, VITE_*, REACT_APP_* and PUBLIC_* are designed to be embedded in browser code. Remove the key from source maps and build artefacts, invalidate the CDN cache, and search the repository history and deployment logs for the redacted prefix shown in the finding. Add secret scanning to pre-commit and CI, then re-run this scanner and the broader website vulnerability scanner. If the key was in a public .env or backup rather than a bundle, also follow the containment steps in Exposed Files and Directories.

Prompt for an AI Hand this check to an assistant Sign in to copy it
The first few lines
You are a senior web engineer. I ran a security and SEO scanner against my site and it reported the finding below. Fix it properly rather than suppressing the symptom.

Finding: Exposed LLM API key in public client-side code (critical severity)
Scanner check id: exposed-llm-api-key
18 more lines, including the evidence and the exact fix

The rest of this prompt names the pages and line numbers we found the problem on, the configuration to change, and the constraints a good answer has to respect. It is free, it just needs an account so the work is not scraped wholesale.

Sign in with Google

Signing in is free and takes one click. We store your email address and nothing else.

References

  1. OWASP Secrets Management Cheat Sheet
  2. OpenAI: API key safety
  3. Google Cloud: Best practices for managing API keys
  4. GitHub: About secret scanning

Related guides