Guide

An API key leaked into your app. What to do in the next ten minutes

The instinct is to delete the line and redeploy. That fixes the file and does nothing about the key, which is the part that’s actually exposed. Order matters more than speed here.

Why deleting it isn’t enough

A key in your frontend code has been downloaded by every visitor, every bot and every crawler that has loaded your site. It may sit in someone’s browser cache, in a CDN edge node, in the Internet Archive, and — if your repo is public — in git history, where deleting the current version changes nothing.

The only action that closes it is revoking the key at the provider. Everything else is housekeeping.

The order

  1. 01

    Rotate the key

    In the provider's dashboard — Stripe, OpenAI, Supabase, wherever it came from — issue a new key and revoke the old one. Not later, not after you've tidied the code. The old key works until you revoke it, and everything else you do is theatre until this is done.

  2. 02

    Put the new one somewhere the browser can't reach

    An environment variable read by a server route or an edge function. If your builder offers 'secrets' or 'environment variables', that's the place. If the value ends up anywhere in your frontend code, you've just repeated the mistake with a fresher key.

  3. 03

    Move the call, not just the key

    The reason the key was in the browser is that the browser was making the call. Moving the key without moving the call breaks your app, so you'll be tempted to move it back. The request has to happen on a server that holds the key and returns only the result.

  4. 04

    Check what it was used for

    Provider dashboards keep request logs. Look at the window between when the key shipped and when you rotated it, for usage you can't account for — unfamiliar volumes, unfamiliar times, requests you didn't write.

  5. 05

    Re-check the live app

    Not the code — the deployed site. Old builds, cached bundles and preview deployments can outlive the fix. The only thing that proves it's gone is fetching the site the way a stranger does and finding nothing.

Which keys actually matter

Not every key in your bundle is a problem, and panicking about the harmless ones makes it harder to see the real one.

  • Meant to be publicStripe publishable keys (pk_live_…), Supabase anon keys, Firebase web config, Google Maps keys restricted to your domain. These are designed to ship. Check the restrictions, don't panic.
  • Never publicAnything labelled secret, service_role, private or admin — sk_live_…, sb_secret_…, service_role JWTs, database connection strings with a password in them. One of these in a bundle is an emergency.

A key that’s meant to be public can still be misconfigured. A Google API key with no domain restriction is someone else’s free quota; a Firebase config with open database rules is the same problem as an open Supabase table.

How it got there

Almost always the same way: you asked for a feature that needed a third-party service, and the fastest thing that worked was calling that service straight from the page. It ran, the preview looked right, and nothing anywhere said “this value is now public.”