TL;DR: Some API keys are built to be seen. Stripe's pk_live key and the Supabase anon key ship in your page code on purpose. Other keys grant full control. sk_live, service_role, and AWS keys must never reach the browser. The rule is simple. A public key is safe only when a guardrail somewhere else does the real work. Row-level security. Domain limits. Scopes. AI coding tools mix the two kinds up all the time. This guide gives you the mental model, then shows how to scan your live URL free in about 30 seconds.
Founders ask us one question more than any other. "My key shows up in the page source. Is that bad?" The honest answer: it depends on which key. Some keys in your page source are fine. One wrong key in the same spot is a critical breach.
This guide shows you how to tell them apart in seconds. No security background needed.
What are the two kinds of API keys and the one rule that separates them?
Secret exposure in client-accessible code is a formally documented vulnerability. OWASP A02:2021 — Cryptographic Failures covers hardcoded and exposed secrets. CWE-312 (Cleartext Storage of Sensitive Information) is the MITRE weakness entry that applies when API keys appear in source bundles or publicly readable environment files.
Launch Ready Code benchmark: most vibe-coded applications we scan have at least one API key or secret exposed in client-accessible JavaScript bundles or publicly readable environment files. — LRC scan data, 2026
Launch Ready Code has scanned 700+ applications. Most ship with at least one critical finding, and the average Launch Readiness Score is ~44 out of 100 — LRC scan data, 2026.
Every API key answers one question for a server: who is asking? But keys split into two families. The split is about what happens if a stranger gets one.
Public tokens only name your app. They point requests at your account. They grant little or no power on their own. The vendor expects them to be seen. Stripe even calls its version a "publishable" key.
Private secrets are different. They prove authority. Whoever holds one can act as you. Charge cards. Read your database. Delete your files. There is no safe way to expose one. Not even for a minute.
Here is the one rule to remember. A public token is safe only because a guardrail somewhere else does the real work. Take the guardrail away and the "safe" key turns into a master key. That is where AI-built apps get burned.
Which API keys are built to be public?
These keys belong in your page code. Seeing them in your bundle is not a finding. It is the design.
Stripe publishable key (pk_live_...). It creates payment tokens in the browser. It cannot charge a card, send a refund, or read your balance. Every Stripe checkout page on the internet exposes one. The guardrail is Stripe's own API: the powerful calls all demand the secret key instead.
Supabase anon key. This one surprises people. It is a signed token with the role "anon" baked in. It ships with every Supabase-backed app. Public by design. The guardrail is row-level security. RLS rules on your tables decide what the anon role can see. With RLS on, the key is a doorbell. With RLS off, it is the front door key. Our own scanner treats it exactly that way. We wrote a full guide on the anon key vs the service_role key.
Browser keys for maps and analytics. Google Maps keys, analytics IDs, and tokens like them are meant for client code. The guardrail is a domain limit. The key only works on requests from your site. Set the limit, or a stranger can burn your quota from theirs.
Which API keys must never reach the browser?
These are the keys that turn a page-source peek into a breach.
Stripe secret key (sk_live_...). Full account control. Charges, refunds, payouts, customer data. Our scanner flags it as critical on sight — the rule sits at scanner.js:81, and there is no "unless" clause.
Supabase service_role key. The anon key's evil twin. It skips row-level security entirely. Every table, every row, no questions asked. It exists so your backend can do admin work. In a browser bundle it hands your whole database to anyone who opens dev tools.
Cloud and platform credentials. AWS access keys, GitHub tokens, OpenAI keys, SendGrid keys. Each one is an open tab on your bill or your code. Our detection list treats nearly all of them as critical. No context needed.
One more thing founders miss. Rotating a leaked key is not the end of the story. Our gitleaks scanner marks any secret found in a public asset as a P0 exposure "regardless of rotation status." The note sits right at the top of gitleaksScanner.js. Why so strict? Because the leak proves the pipeline puts secrets in public files. Rotate the key and the same pipeline leaks the next one. If this has already happened to you, here is what to do in the first hour.
Which keys are safe only when restricted?
Some keys sit between the two families. A Google API key (AIza...) is the classic case. Locked to your domain and your APIs, it is a public token. Left open, it is a quota thief's payday. Our scanner scores these as medium for that exact reason. The pattern at scanner.js:89 carries the comment "often domain-restricted."
The same test works everywhere. The question is never "is this key visible?" The question is "what can a stranger do with it?" Walk through it. Can they spend money, read private data, or act as you? Critical. Can they burn quota or spam a form? Medium. Can they do nothing unless a guardrail fails first? That key was built to be public.
Why AI-Built Apps Mix These Up?
AI coding tools make this mistake easy to ship. Three patterns do most of the damage.
The env-var illusion. Builders put keys in env vars and feel safe. But in front-end frameworks, some env vars are public by default. Anything with a VITE_ or NEXT_PUBLIC_ prefix gets copied into the JavaScript bundle at build time. The name feels private. The value ships to every visitor. We wrote up the Bolt.new version of this trap in our environment variables guide.
The helpful completion. Ask an AI assistant to "call the API" and it writes a working call. The key sits inline, right there in client code. The code runs. The demo works. Tools like Copilot learned from public repos. This pattern is everywhere there. Working is not the same as safe. Our Copilot security audit guide covers the other habits that ride along.
The missing guardrail. The subtlest one. The AI exposes a key that is fine to expose — the Supabase anon key — but never writes the RLS rules that make it fine. Our scanner's platform notes are blunt about it. The anon key is public by design. RLS is your only access-control layer. We test that live with the app's own anon key. An open database scores as a critical finding (scanner.js:336 — the CVE-2025-48757 class). The key was never the problem. The missing guardrail was.
What a Scan Actually Checks?
Here is how this mental model runs in practice. Our scanner reads your public pages and bundles. Any visitor can fetch the same files. Then it applies the split you just learned.
A pk_live key in your bundle? That is not a finding at all. Our platform fingerprinter uses it only as a clue that you run Stripe (platformFingerprintScanner.js:110). An sk_live key in the same bundle is an instant critical. Same page source, opposite verdicts. The model scores power, not visibility.
Then it checks the guardrails. Supabase found? Test whether the anon role can list tables. Google key found? Note whether it looks locked down. Secrets in a public file? Critical, rotation or not. Across the first 100 vibe-coded SaaS, websites, and apps we scanned, the average Launch Readiness Score was 42 out of 100. Exposed keys were among the most common reasons. If keys are your worry, start with our guide to finding exposed secrets in AI bundles.
What is Founder's 5-Minute Checklist?
You can do a rough version of this today.
1. Open your own page source. View source, then search for sk_live, service_role, AKIA, and ghp_. Any hit is a drop-everything problem.
2. Check your env-var prefixes. List every variable that starts with VITE_ or NEXT_PUBLIC_. Each one ships to the browser. Confirm each value is a true public token.
3. Verify the guardrails. Supabase? Confirm RLS is on for every table. Google keys? Confirm domain limits. Stripe? Confirm the secret key lives only on the server.
4. Check where your tokens sit. Auth tokens have a public-vs-private split too. A JWT in localStorage can be read by any script on the page. Our guide on JWT storage covers it.
5. Scan the live app. Your live URL is what attackers see. The free scan checks it in about 30 seconds and returns a score out of 100. Want the full picture across security, reliability, performance, and monitoring? The Launch Readiness Audit is a $499 one-time report. And once you handle user data, key hygiene becomes a paperwork question too. It is one of the first things reviewed in a compliance check. So are duties like AI disclosure under the EU AI Act.
Frequently Asked Questions
Is it safe to put an API key in client code?
Only if the key was designed for it. Publishable keys, anon keys, and domain-locked browser keys are built to be seen. Secret keys are not. If a stranger with the key could spend money, read private data, or act as you, it must stay on the server. When in doubt, check the vendor's docs for the word "publishable" or "public."
What is the difference between a publishable key and a secret key?
A publishable key names your account and grants almost no power. Stripe's pk_live key can create payment tokens but cannot charge cards or read data. A secret key proves authority. Stripe's sk_live key can charge, refund, and read everything. Publishable keys belong in the browser. Secret keys stay on your server.
Is the Supabase anon key safe to expose?
Yes — but only when row-level security is on for every table. The anon key ships with every Supabase app and is public by design. RLS rules are the guardrail. They decide what it can read. With RLS off or missing, the same key lets anyone list and read your tables. That combo is a critical finding in our scans.
I leaked a secret key but rotated it. Am I safe now?
Rotate first, always. But rotation alone does not close the case. The leak proves your build pipeline puts secrets into public files, so the next key can leak the same way. Find the path the key took. An env-var prefix, a hardcoded line, or a committed file. Fix that too. Our scanner flags secrets in public assets as critical regardless of rotation status for exactly this reason.
Why do AI coding tools expose keys so often?
Because the unsafe version works. AI assistants learned from public code. In public code, keys sit inline in API calls. A suggestion with the key in client code runs fine in the demo. The tools also lean on env vars with public prefixes like VITE_ and NEXT_PUBLIC_. Those copy values into the bundle. Nothing breaks until someone reads your page source.
How do I check my live app for exposed secrets?
Run a scan against your live URL. Our free scan fetches your public pages and bundles. It checks them for secret-key patterns, open database access, and missing guardrails. It takes about 30 seconds and needs no code access. You get a Launch Readiness Score out of 100 and the list of gaps to fix.
Research sources
- OWASP Foundation — OWASP Top 10 Web Application Security Risks (2021), the industry-standard vulnerability taxonomy referenced for all security categories in this article
- MITRE Corporation — CWE Top 25 Most Dangerous Software Weaknesses (2024), the weakness classification used to rank and prioritise code-level findings
- NIST National Vulnerability Database — NVD CVE severity ratings; all CVSS scores cited here are drawn from published NVD records
- Jai Mittal, Founder & CTO, Launch Ready Code — Proprietary data from 700+ AI-built app security audits, 2025–2026. Average Launch Readiness Score: 44/100. Most common critical failures: missing HTTP security headers (83% of scans), no rate limiting on auth endpoints (71%), exposed API keys or secrets (67%), absent database Row Level Security (58%).