The Two Keys at a Glance
Anon Key
Public / Client-safe
- Safe to use in browsers
- Safe to commit to public repos
- Subject to RLS policies
- Limited to anon role permissions
Service Role Key
Secret / Server-only
- Never use in browsers
- Never commit to repos
- Bypasses ALL RLS policies
- Full database access
How They Work
When you create a Supabase project, you get two API keys. They look similar (both are JWTs), but they grant completely different levels of access.
Anon Key: Designed to be Public
The anon key is intentionally designed to be exposed in your frontend code. It's not a secret - your security doesn't depend on hiding it.
// This is SAFE in client-side code
const supabase = createClient(
'https://xxx.supabase.co',
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...' // anon key
)
When a request uses the anon key:
- PostgreSQL sees the connection as the
anonrole - All RLS policies are evaluated
- Only rows matching policy conditions are returned
Service Role Key: Full Access
The service role key bypasses Row Level Security entirely. It's the "admin" key that can do anything.
// This should ONLY be in server-side code
const supabaseAdmin = createClient(
process.env.SUPABASE_URL,
process.env.SUPABASE_SERVICE_ROLE_KEY // NEVER expose this
)
An attacker can read ALL data in your database, modify ANY row, delete entire tables, and create admin accounts. There is no RLS protection.
Comparison Table
| Feature | Anon Key | Service Role Key |
|---|---|---|
| Safe in browser? | Yes | Never |
| RLS applies? | Yes | Bypassed |
| PostgreSQL role | anon |
service_role |
| Use case | Client apps, public APIs | Server functions, admin tasks |
| If exposed | Limited by RLS | Full database access |
When to Use Each Key
Use Anon Key For:
- React/Vue/Svelte/Next.js client-side code
- Mobile apps (React Native, Flutter)
- Public-facing APIs
- Any code that runs in the browser
Use Service Role Key For:
- Server-side API routes (Next.js API routes, Express)
- Supabase Edge Functions that need admin access
- Background jobs and cron tasks
- Database migrations and seeding
- Admin dashboards (backend only)
Common Mistakes
Mistake 1: Using NEXT_PUBLIC_ for Service Key
# WRONG - This exposes the key in your bundle!
NEXT_PUBLIC_SUPABASE_SERVICE_KEY=eyJ...
# CORRECT - No NEXT_PUBLIC_ prefix
SUPABASE_SERVICE_ROLE_KEY=eyJ...
Mistake 2: Using Service Key "Just to Be Safe"
Some developers think using the service key is "more secure." It's the opposite - you're bypassing all your security policies!
Mistake 3: Committing Keys to Git
Even if your repo is private, never commit the service role key. Use environment variables and .env.local files that are gitignored.
How to Check Which Key You're Using
Use our JWT Decoder to inspect any Supabase key:
// Decode the JWT payload
// Anon key will have: { "role": "anon" }
// Service key will have: { "role": "service_role" }
If you're not sure which key is in your code, paste it into our API Key Identifier to instantly find out.
What If My Service Key Is Exposed?
If you've accidentally exposed your service role key:
- Rotate immediately - Go to Project Settings → API and regenerate the key
- Audit your database - Check for unauthorized changes or data access
- Review logs - Look for suspicious queries in Supabase logs
- Update your code - Replace the old key with the new one in all environments
Check if your keys are exposed
Scan your website to find any leaked API keys in your client-side code.
Scan Your Site