apps/docs/content/guides/getting-started/api-keys.mdx
Supabase gives you fine-grained control over which application components are allowed to access your project through API keys.
<Admonition type="tip" title="Looking for your API Keys?">In most cases, you can get the correct key from the Project's Connect dialog, but if you want a specific key, you can find all keys in the Settings > API Keys section of the Dashboard:
</Admonition>API keys provide the first layer of authentication for data access. Auth then builds upon that. This chart covers the differences:
| Responsibility | Question | Answer |
|---|---|---|
| API keys | What is accessing the project? | Web page, mobile app, server, Edge Function... |
| Supabase Auth | Who is accessing the project? | Monica, Jian Yang, Gavin, Dinesh, Laurie, Fiona... |
An API key authenticates an application component to give it access to Supabase services. An application component might be a web page, a mobile app, or a server. The API key does not distinguish between users, only between applications.
There are 4 types of API keys that you can use with Supabase:
| Type | Format | Privileges | Availability | Use |
|---|---|---|---|---|
| Publishable key | <span className="whitespace-nowrap!">sb_publishable_...</span> | Low | Platform | Safe to expose online: web page, mobile or desktop app, GitHub actions, CLIs, source code. |
| Secret keys | <span className="whitespace-nowrap!">sb_secret_...</span> | Elevated | Platform | Only use in backend components of your app: servers, already secured APIs (admin panels), Edge Functions, microservices, etc. They provide full access to your project's data, bypassing Row Level Security. |
<span className="whitespace-nowrap!">anon</span> | JWT (long lived) | Low | <span className="whitespace-nowrap!">Platform, CLI</span> | Legacy version of publishable keys. |
<span className="whitespace-nowrap!">service_role</span> | JWT (long lived) | Elevated | <span className="whitespace-nowrap!">Platform, CLI</span> | Legacy version of secret keys. |
Supabase has changed the way keys work to improve project security and developer experience. You can read the full announcement.
anon and service_role keys are based on the project's JWT secret. They are generated when your project is created and you can only change them when you rotate the JWT secret. This can cause significant issues in production applications. You should now use the sb_publishable_xxx and sb_secret_xxx keys instead.
You can still find legacy keys in the Legacy anon, service_role API keys tab of the Settings > API Keys section of the Dashboard:
</Admonition>Publishable keys identify the public components of your application. Public components run in environments where it is impossible to secure any secrets. These include:
These environments are always considered public because anyone can retrieve the key from the source code or build artifacts.
Using a publishable key does not mean that your user is anonymous. You can authenticate your application with the publishable key, while your user is authenticated (via Supabase Auth) with their personal JWT:
| Key | User logged in via Supabase Auth | Postgres role used for RLS, etc. |
|---|---|---|
| Publishable key | No | anon |
| Publishable key | Yes | authenticated |
Publishable keys are not intended to protect from the following, since key retrieval is always possible from a public component:
When using a publishable key, access to your project's data is guarded by Postgres via the built-in anon and authenticated roles. For full protection make sure:
anon and authenticated roles.Your project's Security Advisor constantly checks for common security problems with the built-in Postgres roles. Make sure you carefully review each finding before dismissing it.
Unlike publishable keys, secret keys allow elevated access to your project's data. It is meant to be used only in secure, developer-controlled components of your application, such as:
Never expose your secret keys publicly. Your data is at risk. Do not:
localhost.Ensure you handle them with care and using secure coding practices.
</Admonition>Secret keys authorize access to your project's data via the built-in service_role Postgres role. By design, this role has full access to your project's data. It also uses the BYPASSRLS attribute, skipping any and all Row Level Security policies you attach.
The secret key is an improvement over the old JWT-based service_role key, and we recommend using it where possible. It adds more checks to prevent misuse, specifically:
User-Agent header) and it will always reply with HTTP 401 Unauthorized.Below are some starting guidelines on how to securely work with secret keys:
service_role has been leaked or compromised?Don't rush if this has happened, or you are suspecting it has. Make sure you have fully considered the situation and have remediated the root cause of the suspicion or vulnerability first. Consider using the OWASP Risk Rating Methodology as an easy way to identify the severity of the incident and to plan your next steps.
To rotate a secret key (sb_secret_...), use the Settings > API Keys section of the Dashboard to create a new secret API key, then replace it with the compromised key. Once all components are using the new key, delete the compromised one.
Deleting a secret key is irreversible and once done it will be gone forever.
If you are still using the JWT-based service_role key, replace the service_role key with a new secret key instead. Follow the guide from above as if you are rotating an existing secret key.
As the publishable and secret keys are no longer JWT-based, there are some known limitations and compatibility differences that you may need to plan for:
Authorization: Bearer ... header, except if the value exactly equals the apikey header. In this case, your request will be forwarded down to your project's database, but will be rejected as the value is not a JWT.anon and service_role JWT-based API keys. You will need to use the --no-verify-jwt option when using publishable and secret keys. The Supabase platform does not verify the apikey header when using Edge Functions in this way. Implement your own apikey-header authorization logic inside the Edge Function code itself.