ShipGuardScan a site

Bolt.new: How to Fix Exposed API Keys in Your Frontend

Updated 2026-10-17

Bolt.new commonly scaffolds Vite + React frontends. In Vite, any variable prefixed with VITE_ is inlined into the JavaScript that ships to browsers. When a prompt like "add OpenAI" leads to VITE_OPENAI_API_KEY, the secret becomes public the moment you deploy.

Find what shipped

Build your app and search the output for key prefixes. Anything you find in dist/ is readable by every visitor.

npm run build
grep -rEo "sk_live_[0-9A-Za-z]+|sk-proj-[A-Za-z0-9_-]+|sk-ant-[A-Za-z0-9_-]+|re_[A-Za-z0-9_]+" dist/ | sort -u

Move the call behind a server route

Create a small serverless function (Netlify/Vercel function, Supabase Edge Function, or an Express route) that holds the key in a server-only environment variable and calls the provider. The browser calls your function instead.

Add authentication and a rate limit to that function — otherwise you have only moved the open door, not closed it.

// supabase/functions/chat/index.ts (Deno)
Deno.serve(async (req) => {
  const { prompt } = await req.json();
  const r = await fetch("https://api.openai.com/v1/responses", {
    method: "POST",
    headers: { Authorization: `Bearer ${Deno.env.get("OPENAI_API_KEY")}`, "Content-Type": "application/json" },
    body: JSON.stringify({ model: Deno.env.get("OPENAI_MODEL"), input: String(prompt).slice(0, 4000) }),
  });
  return new Response(await r.text(), { status: r.status, headers: { "Content-Type": "application/json" } });
});

Rotate, then redeploy

Removing the key from code does not un-leak it: old bundles may be cached by CDNs, browsers and archive crawlers. Revoke the exposed key in the provider dashboard, issue a new one, and store it only in server environment settings.

Checklist

  • No VITE_ variable holds a secret
  • Provider calls happen in a server function
  • Server function requires auth and is rate limited
  • Exposed keys revoked and replaced
  • Spending limits / usage alerts set at the provider

Copy-paste fix prompt

Find every `import.meta.env.VITE_*` usage that holds a secret (OpenAI, Anthropic, Stripe secret, Resend). Replace each with a call to a new server function that reads the secret from a server-only env var, requires the user's session, and applies a per-user rate limit. Remove the VITE_ variables and list the keys I need to rotate.

FAQ

Does hiding the key in an environment variable fix it?

Only if the variable is server-only. In Vite, VITE_-prefixed variables are bundled into client code, so they are public.

Is my Stripe publishable key a problem?

No. pk_live_ and pk_test_ keys are designed for browsers. Only sk_live_ / rk_live_ keys are secret.

Check your deployed app in ~25 seconds

Free passive scan: leaked keys, Supabase exposure, public config files and headers.

Run a free scan