API Keys
What each Supabase key does, where to find it, and the one rule you must never break.
Your Supabase project has keys. Different keys open different doors. Using the wrong key in the wrong place is one of the most consequential mistakes you can make in a production project — not because your app breaks, but because it exposes your entire database to anyone on the internet.
This page tells you exactly which key goes where, and why.
The Scenario That Makes This Matter
Imagine you manage a multi-story building. You have a lobby door code (anyone can walk in, but they can only go where their access card allows). You also have a master key that opens every room, overrides every lock, and bypasses every security measure.
You give the lobby code to clients. You guard the master key with your life.
Now imagine you accidentally printed the master key code on your company's public website.
That is exactly what happens when a developer puts the Supabase service role key in frontend code. It has happened to real companies. Real databases were emptied. Real users lost their data.
The key system below exists to prevent this.
The Three Keys
Key 1: The Anon / Public Key
Name in the dashboard: anon public
This is the key you put in your frontend code — in your React app, in your .env file, committed (via .env.example) to your GitHub repository as a reference.
It is safe to share. Not because it gives no access, but because it respects RLS. Every request made with the anon key is filtered by your Row Level Security policies. If a user isn't authenticated, they can only see what you've explicitly made public. If they are authenticated, they can only see their own data — because that's what your RLS policies allow.
The anon key is not a skeleton key. It is a lobby access code. The building's internal security (RLS) still applies.
Where it goes:
The NEXT_PUBLIC_ prefix tells Next.js that this variable can be sent to the browser. Vite projects use VITE_ instead:
Key 2: The Service Role Key
Name in the dashboard: service_role
This key bypasses ALL Row Level Security. A request made with this key has unrestricted read and write access to every row in every table in your database — regardless of who the user is, regardless of your RLS policies. Never put this key in frontend code. Never commit it to GitHub. Never send it in a WhatsApp message or email. If this key leaks, your entire database is compromised.
The service role key is used exclusively in:
- Supabase Edge Functions (server-side code that runs on Supabase's servers, not in the user's browser)
- Your own backend server (if you have one — Node.js, Python, etc., running on a server you control)
- Admin scripts you run locally to manage data
The key itself starts with eyJ... (it's a JWT), just like the anon key. You cannot tell them apart by looking at the first few characters. The only way to know which is which is the label in the dashboard.
Where it goes:
In edge function secrets (never in .env):
If you absolutely must use it locally (for an admin script), keep it in a .env.local file that is listed in .gitignore and never add NEXT_PUBLIC_ or VITE_ prefix.
Settings → Edge Functions Secrets — store API keys for third-party services here so your edge functions can access them without exposing them in code.
The rule that never changes, regardless of project:
| Location | Anon Key | Service Role Key |
|---|---|---|
| React frontend code | Yes | Never |
.env file | Yes | Only without public prefix |
| Edge function secrets | Not needed | Yes |
| GitHub repository | Only as .env.example placeholder | Never |
| Shared with team | Yes | Never |
| VaultMate | Yes | Yes |
The Vault — Storing Other Secrets
Beyond the project API keys, Supabase has a built-in Vault (Settings → Vault) for storing arbitrary secrets your Edge Functions need — third-party API keys, webhook signing secrets, anything sensitive.
Vault → Secrets — encrypted at rest, accessible only to your Edge Functions.
Use the Vault for: Razorpay key secret, Resend API key, Sentry read token, MSG91 token, Setu/Digio API keys, Google Cloud service account JSON. Anything you would otherwise be tempted to put in .env.
Key 3: JWT Secret
Name in the dashboard: JWT Secret
This is used for advanced scenarios — creating custom authentication tokens or integrating with external auth systems. You will not need this during training. When you do need it (in a later project), it lives in edge function secrets only, same rules as the service role key.
Where to Find Your Keys
- Go to supabase.com/dashboard
- Open your project
- In the left sidebar, click the Settings icon (gear icon, near the bottom)
- Click API in the settings submenu
You will see:
Project URL — looks like https://abcdefghijklmnop.supabase.co
This is not a key — it's the address of your project's API. You need this alongside every key.
Project API keys — two keys listed:
anon public— this is the anon keyservice_role— hidden by default, click "Reveal" to see it (do this with no one looking over your shoulder)
Copy each one carefully. They are long strings (200+ characters). A partial copy will not work.
eyJ. If it looks shorter or different, go back and copy again. Partial copies are the most common cause of "invalid key" errors.Access Tokens — A Different Kind of Key
The keys above are for your application (your React app, your edge functions) to talk to Supabase.
Access Tokens are for you — specifically for tools that manage your Supabase projects:
- The Supabase CLI (
supabase login) uses an access token so the terminal tool knows who you are - The Supabase MCP server (the Claude Code integration) also uses an access token for the same reason
Both use the same type of token. You can use the same token for both, or create separate ones.
Where to generate:
- Go to supabase.com/dashboard
- Click your avatar in the top-right corner
- Click Account
- Scroll to Access Tokens
- Click Generate new token
- Give it a descriptive name:
CLI + MCP - [your name] - [month year] - Copy the token immediately — it is shown only once
Account → Access Tokens. Click "Generate new token", name it descriptively (include the month and year so you can rotate it later), and copy the value the moment it appears. Once you close this dialog, it's gone.
Copy the access token the moment it's generated. Supabase shows it only once. If you close the page without copying it, you must delete it and generate a new one.
Store it in VaultMate:
- Project: your project name (or "Supabase Account" if it's account-level)
- Category:
Token - Title:
Supabase Access Token - CLI + MCP - Secret: the token value (starts with
sbp_...)
sbp_. If it does not start with sbp_, you copied the wrong credential — the access token is distinct from the project API keys.The Complete Key Inventory
When you start any new project, you should have all of these located and stored:
| Key | Found In | Stored In | Used By |
|---|---|---|---|
| Project URL | Settings → API | .env | React app, all tools |
| Anon key | Settings → API → anon public | .env | React app (frontend) |
| Service role key | Settings → API → service_role | Supabase secrets + VaultMate | Edge functions only |
| Access token | Account → Access Tokens | VaultMate only | CLI, MCP server |
Setting Up Your .env File
For a Next.js project:
For a Vite project (React):
And create a .env.example file (safe to commit — contains placeholders only):
Confirm .env and .env.local are in your .gitignore:
Using the Keys in Code
Once your .env file has the keys, your Supabase client reads them:
This file is the single place where the Supabase client is created. Every other file in your project imports from this file — not directly from @supabase/supabase-js.
Once you add TypeScript types (covered in the next section), this createClient call will also include your database types, giving you autocomplete on table and column names throughout your project.