Is your Supabase anon key safe in the browser? Yes — if RLS is real
The anon key belongs in every bundle. What matters is whether policies hold. Decode-and-verify method inside; free scan calibrates.
This page will not tell you to rotate anything, because most visitors arriving here have found something that is supposed to be there. That distinction — expected vs emergency — is where scanner credibility lives or dies.
Decode it yourself in ten seconds, then verify the policies that make publicity safe.
What the Supabase anon key is
Anon keys are JWTs identifying unauthenticated requests. They gate nothing by themselves; row-level security decides reachability.
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJvbGUiOiJhbm9uIiwiaWF0IjoxNzU0MDAwMDAwfQ.FAKE_SIGNATURE_EXAMPLE_ONLY
Decode payload (no signature needed): "role":"anon" confirms classification instantly. service_role in the same field flips the story entirely.
What it grants
With solid RLS:
- Only rows policies expose to anonymous sessions.
- Rate-limited, logged, revocable via policy edits rather than secret rotation.
How it ends up in a bundle
Three arrival routes cover nearly every case we see:
- Every correctly-wired Supabase client — presence alone is not a finding.
Does it belong in a browser?
Public-by-design. Reporting anon keys as critical is malpractice that trains teams to ignore real alerts — the failure mode that gives scanners bad reputations.
Rotate it
N/A normally. If RLS proves absent/tautological, fix policies first; key rotation is orthogonal theater here.
Find it in seconds
Open DevTools on the deployed site and search the built assets for eyJ. If the search hits, the credential shipped; if it does not, check the chunks loaded on authenticated or interactive views, not just the landing page — the calling code often sits behind a route.
The faster path is to let a machine do the fetching. KeyDrift downloads the same JavaScript a visitor gets — HTML, every referenced chunk including ones named only in the route manifest, and the server-streamed data frameworks inline into the document — and reports credentials with a masked prefix, a fingerprint, and the exact file they live in. Paste your deployed URL into the scanner; no account needed.
Make sure it stays gone
It bears saying because it happens constantly: the fix holds until the next prompt that needs the query to return rows. Drift monitoring exists for precisely this — it diffs consecutive scans and pages you when a previously resolved finding reappears, naming the regression as a regression rather than repeating the first alert.
When an anon key IS bad news
Only in combination: missing policies, USING(true) tautologies, security-definer functions bypassing checks. Those are database-posture problems — our sibling product VibeGuard tests exactly that surface; one mention, no more.
The bigger picture
It helps to name the economics honestly. Fixing this class of leak costs minutes when caught at deploy time and days when caught at invoice time, because by then the credential has been harvested, validated, resold or drained — often all four. Detection latency is the entire game, which is why the monitoring half of KeyDrift exists alongside the scanning half.
How KeyDrift reports this exact finding
When KeyDrift finds this on your deployment, the report shows a masked value (first 8 and last 4 characters only), a salted fingerprint for tracking, the exact chunk filename carrying it, and a severity with written rationale. Public-by-design neighbours — anon keys, publishable keys, Firebase web constants — appear as informational context rather than noise, because knowing what should be there is what makes the real findings credible.
Manual check, step by step
A five-minute version you can run anywhere: view-source on the landing page, copy every src= script URL, fetch each and search the results for eyJ. It misses manifest-only chunks and streamed payloads — which is precisely the gap between "I checked" and "it is clean" — but it catches the loud majority and builds the pattern-recognition that makes scanner output legible.
Close the loop with monitoring
Monitoring closes the loop that one-time verification leaves open. A scheduled scan refetches everything, diffs against history, and fires only on transitions: created, regressed, resolved. Regression alerts matter most here — they fire when a previously fixed finding returns, which in agent-era codebases is less a possibility than a schedule.
Common questions
KeyDrift reports my anon key as info — why show it at all?
Transparency: seeing public credentials listed (and excluded from counts) teaches the taxonomy that makes critical alerts credible.
Should I restrict anon key via referrers?
Supabase manages auth differently than Firebase; policy quality is your control plane here.
Run a free scan at keydrift.dev/scan — paste a URL or the bundle source itself, no account. Findings arrive masked, with the exact chunk they live in.