How to fix a hardcoded API key in client-side JavaScript
An API key is hardcoded in your client-side JavaScript. Anyone who views your site source can extract it. The fix has three steps: (1) rotate the key immediately — assume it is compromised; (2) move the API call to your server (API route, server action, or serverless function) so the key never ships to the browser; (3) add guardrails — prefix only server secrets without NEXT_PUBLIC_, scan builds for leaked keys. Do not try to "obfuscate" the key — it will be found.
Why it matters
Attackers run automated bundle-scanning tools against popular sites. An exposed OpenAI, Stripe, or cloud key is drained within hours of publication — sometimes minutes. Your bill can run to thousands of dollars before you notice.
How to check
- 01View your site source (Ctrl+U) and search for common patterns: `sk_`, `sk-`, `AKIA`, `xoxb-`, `ghp_`, `pk_live_`.
- 02Check DevTools → Sources for your bundle files and search the same patterns.
- 03Or download the JS with `curl https://your-site.com/_next/static/chunks/*.js` and grep.
Or let SafeToShip check it for you in 60 seconds:
How to fix it
Next.js — move to server
Create a Route Handler that calls the third-party API with the key. Your client calls your handler, never the third party directly.
// app/api/ai/route.ts — SERVER ONLY
import { NextRequest } from 'next/server';
export async function POST(req: NextRequest) {
const { prompt } = await req.json();
const res = await fetch('https://api.openai.com/v1/chat/completions', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.OPENAI_API_KEY}`, // no NEXT_PUBLIC_
'Content-Type': 'application/json',
},
body: JSON.stringify({ model: 'gpt-4o-mini', messages: [{ role: 'user', content: prompt }] }),
});
const data = await res.json();
return Response.json(data);
}
// Client calls /api/ai, never api.openai.com directly.Lovable
Prompt Lovable: 'Move all third-party API calls to server-side functions. No API keys should exist in the frontend code. Rotate any exposed keys in the provider dashboard first.'
AI prompt
Copy-paste into your AI tool
Paste this prompt into Cursor, Lovable, Bolt, v0, or Claude Code and it will walk through the fix for your specific codebase.
Find every API key in my client-side code. For each one: (1) tell me which service it belongs to so I can rotate it; (2) determine if the API call needs to happen in the browser (it usually does not); (3) move the call to a server-side route handler or server action that reads the key from an env var without NEXT_PUBLIC_ prefix; (4) update the frontend to call my endpoint instead. Provide the rotation URL for each service.FAQ
Frequently asked questions
- But the user needs to make the API call from their browser for latency.
- Then use a signed, time-limited credential (like S3 presigned URLs, Twilio Access Tokens, or OpenAI's ephemeral keys) generated server-side per-request. Never the raw master key.
- My framework variables all start with NEXT_PUBLIC_. Is that fine?
- For public values yes (Stripe publishable key, Supabase anon key). For secret values no — `NEXT_PUBLIC_` inlines the value into the browser bundle. Use `NEXT_PUBLIC_` only for values that are meant to be public.
Related fix guides
Fix these too
Exposed Stripe secret key
A leaked Stripe secret key means an attacker can charge your customers, refund payments, or drain your account. Rotate immediately.
Read moreExposed OpenAI API key
OpenAI keys in client code get drained fast — attackers use them to run expensive models on your bill. Here is how to lock it down.
Read moreExposed .env file
An exposed .env file is a critical leak — it contains API keys, database URLs, and secrets. Here is why it happens in vibe-coded apps and how to lock it down.
Read moreExposed Supabase service key
The service role key bypasses all security in Supabase. If it is in your client code, an attacker has full database access. Here is how to find and fix it.
Read moreFree tools
Check this yourself
Platform guides
Building on these platforms?
Lovable security
Lovable makes it easy to ship fast, but AI-generated backends often ship with open Supabase tables and leaked API keys. Scan your Lovable app before your users find out.
Read moreBolt security
Bolt generates full-stack apps in seconds, but speed can leave security gaps. Exposed environment files and missing CORS configuration are common in Bolt projects.
Read moreCursor security
Cursor helps you write code faster with AI, but AI-assisted code can introduce subtle security issues. Missing headers, exposed files, and insecure cookies slip through easily.
Read morev0 security
v0 generates beautiful React components, but when you connect a backend, security gaps appear. Missing CSP headers and insecure cookie settings are the most common.
Read moreNext.js security
Next.js is the most popular React framework, but even experienced developers miss security headers and accidentally expose server files in production.
Read moreScan your site for this and 50+ other issues
Free scan. Results in 60 seconds. No account required.