Almost nobody loses a key to an attacker. They publish it themselves, in a commit, a screenshot, or a bundle of JavaScript sent to every visitor. The people who find it are not watching you personally. They run scanners across every public repository on GitHub, and a key posted in public is typically found and used within minutes.

The idea

A secret is any string that grants access on its own: an , a database password, a , a webhook signing secret. Because it works for whoever holds it, the only meaningful question after an exposure is who has held it, and the only reliable fix is to make the old value stop working.

code
  where secrets end up in public

  your machine          published surface        who reads it
  ------------          -----------------        ------------
  .env  ----(committed)---> git history  ------> automated scanners
  chat  ----(pasted)------> public issue -------> search engines
  code  ----(NEXT_PUBLIC_)--> browser bundle ----> anyone who opens devtools
  screen --(recorded)-----> demo video --------> anyone who pauses the frame

How it works

  • Committed to git. A .env file gets added because .gitignore was missing or wrong. Deleting the file in a later commit does not help: git keeps every earlier version, so the key stays readable in the history and in every clone and fork that already pulled it. GitHub's own guidance on removing sensitive data says to treat the credential as compromised and rotate it, because rewriting history cannot reach copies other people already have.
  • Pasted into a public place. A stack trace with the connection string in it, dropped into a GitHub issue or a public Discord while asking for help.
  • Shipped to the browser. Frontend code runs on the visitor's machine, so anything compiled into it is public. In Next.js, any environment variable named with the NEXT_PUBLIC_ prefix is inlined into the JavaScript bundle by design. A secret given that prefix is not hidden by minification.
  • Filmed. A terminal, a dashboard, or an editor tab visible in a demo video or a conference screenshare. Frames can be paused.

What to do

  1. Rotate first. Issue a new key in the provider's dashboard, deploy it, then revoke the old one. Do this before you work out how it leaked. Investigation takes hours and rotation takes minutes, and until the old value is dead the exposure is still live.
  2. Keep secrets in .env.local and confirm it is ignored. Run git check-ignore -v .env.local. If it prints nothing, the file is not ignored and your next git add . will commit it.
  3. Check what actually reaches the browser. Search your source for NEXT_PUBLIC_ and confirm every match is something you would print on a billboard. The publishable keys from Stripe and Supabase belong there. Service-role and secret keys never do.
  4. Turn on push protection in the repository's settings on GitHub, so a recognised key format is blocked at push time rather than found later.

Where it breaks

Rotation has a cost you should know about before the bad night: swapping a database password or a webhook secret takes production down until every place that uses it has the new value, so write down where each secret lives now rather than discovering it under pressure. Scanners only catch formats they recognise, so a self-issued token or a plain password can sit in a repository for years without a single alert. And rotation limits future damage without undoing past damage. If a key with read access to your database was public for a week, the copy someone took still exists, which makes it a data exposure question rather than only a key question.