NEXT_PUBLIC secret leakage: how to audit Supabase keys in Next.js
How to audit Next.js environment variables, Supabase anon/service-role keys, bundle exposure, server-only code paths, and build-time leak prevention.
Inside this guide
- NEXT_PUBLIC variables are bundled for the browser and must never contain secrets.
- The Supabase anon key is public; the service-role key is not.
- Audit source, build output, logs, and runtime env before production.
Know which keys are meant to be public
The Supabase anon key can be public because permissions are enforced by grants and RLS. The service-role key bypasses RLS and belongs only on trusted servers.
In Next.js, any variable prefixed with NEXT_PUBLIC_ is exposed to client code. Do not put secret keys, database URLs, webhook secrets, or service-role tokens behind that prefix.
Search source and build output
A source search catches obvious mistakes. A build-output search catches accidental imports, copied config, and generated files. Do both before launch and after env changes.
rg -n "service_role|SUPABASE_SERVICE|postgres://|NEXT_PUBLIC_.*SECRET|sk_" .
npm run build
rg -n "service_role|SUPABASE_SERVICE|postgres://|sk_" .nextKeep server-only Supabase clients server-only
Create separate browser and server clients. The browser client uses the anon key. Server routes, server actions, cron jobs, and webhooks may use privileged credentials after authorization.
Do not import server clients into client components. Add lint rules or file naming conventions if your team keeps making that mistake.
Rotate when uncertain
If a service-role key may have shipped to a browser bundle, logs, screenshots, or analytics, rotate it. Then update server environments and redeploy.
Cleanup without rotation leaves a long-lived credential active. Treat exposure response as a security incident, not a cosmetic refactor.
FAQ
Is NEXT_PUBLIC_SUPABASE_ANON_KEY safe?
Yes, as long as grants and RLS are correct. It is designed for browser use.
Can I use the service-role key in middleware?
Only if it stays server-side and the route is carefully authorized. Never expose it to the browser.