Understanding API keys
Supabase gives you fine-grained control over which application components are allowed to access your project through API keys.
Looking for your API Keys?
In most cases, you can get the correct key from your project's Connect dialog. To pick a specific key, open the Settings > API Keys section of the Dashboard.
API keys provide the first layer of authentication for data access. Supabase Auth builds on top of that. This table covers the difference:
| Responsibility | Question | Answer |
|---|---|---|
| API keys | What is accessing the project? | A web page, a mobile app, a server, or an Edge Function |
| Supabase Auth | Who is accessing the project? | An individual signed-in user |
Overview#
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. An API key doesn't distinguish between users but between applications.
Supabase supports four types of API keys:
| Type | Format | Privileges | Availability | Use |
|---|---|---|---|---|
| Publishable key | sb_publishable_... | Low | Platform | Safe to expose online: web page, mobile or desktop app, GitHub actions, CLIs, source code. |
| Secret keys | sb_secret_... | Elevated | Platform | Only use in backend components of your app, such as servers, APIs with their own authorization checks, Edge Functions, and microservices. They provide full access to your project's data, bypassing Row Level Security. |
anon | JWT (long-lived) | Low | Platform, CLI | Legacy version of publishable keys. |
service_role | JWT (long-lived) | Elevated | Platform, CLI | Legacy version of secret keys. |
Creating new keys doesn't revoke your legacy keys
Both key types work simultaneously. Creating publishable and secret keys adds them alongside your existing anon and service_role keys without affecting them, so your legacy keys keep working. They remain valid until you disable them in the Settings > API Keys section of the Dashboard, which is a separate step. See Migrating to new API keys for the full process.
Changes to API keys
Supabase has changed the way keys work to improve project security and developer experience. You can read the full announcement on GitHub.
They will be deprecated by the end of 2026, and you should now use the publishable (sb_publishable_xxx) and secret (sb_secret_xxx) keys instead.
In most cases, you can get keys from the Project's Connect dialog, but if you want a specific key, you can find them in the Settings > API Keys section of the Dashboard.
- For new keys, open the API Keys tab, if you don't have a publishable key already, click Create new API Keys, and copy the value from the Publishable key section for client-side operations. For server-side operations, copy the value from the Secret keys section.
- For legacy keys, copy the
anonkey for client-side operations and theservice_rolekey for server-side operations from the Legacy API Keys tab.
Publishable keys#
Publishable keys identify the public components of your application. Public components run in environments where you can't keep a secret. These include:
- Web pages, where the key is bundled in source code.
- Mobile or desktop applications, where the key is bundled inside the compiled packages or executables.
- CLI, scripts, tools, or other pre-built executables.
- Public APIs that return the key without requiring authorization first.
These environments are always considered public because anyone can retrieve the key from the source code or build artifacts.
Interaction with Supabase Auth#
Using a publishable key doesn't mean your user is anonymous. Your application authenticates with the publishable key while your user authenticates separately through Supabase Auth with their own JWT:
| Key | User logged in via Supabase Auth | Postgres role |
|---|---|---|
| Publishable key | No | anon |
| Publishable key | Yes | authenticated |
Security considerations#
Publishable keys aren't intended to protect against the following, because anyone can retrieve a key from a public component:
- Static or dynamic code analysis and reverse engineering attempts.
- Use of the Network inspector in the browser.
- Cross-site request forgery, cross-site scripting, phishing attacks.
- Man-in-the-middle attacks.
When you use a publishable key, Postgres guards access to your project's data through the built-in anon and authenticated roles. For full protection, confirm that:
- You have enabled Row Level Security on all tables.
- You regularly review your Row Level Security policies for permissions granted to the
anonandauthenticatedroles. - You don't change the roles' attributes without understanding what the change does.
Your project's Security Advisor checks for common security problems with the built-in Postgres roles. Review each finding carefully before you dismiss it.
What secret keys allow access to#
Unlike publishable keys, secret keys allow elevated access to your project's data. Use them only in secure, developer-controlled components of your application, such as:
- Servers that run their own authorization checks, such as Edge Functions, microservices, and web servers.
- Periodic jobs, queue processors, topic subscribers.
- Admin and back-office tools that run authorization checks first.
- Data processing pipelines, such as for analytics, reports, backups, or database synchronization.
Exposing a secret key puts all of your project's data at risk. Don't:
- Add a secret key to a web page, a public document, source code, or a bundled mobile, desktop, or CLI package.
- Send one over chat, email, or SMS.
- Use one in a browser, even on
localhost. - Pass one in a URL or query parameter, because those are often logged.
- Pass one in a request header before you sanitize your logs.
- Log even an apparently invalid API key. A typo today can reveal the real key later.
- Reveal, copy, or use one on a device you don't own or control, or on a device without full-disk encryption. Public computers and borrowed laptops both count.
Handle secret keys using secure coding practices.
Secret keys authorize access to your project's data through the built-in service_role Postgres role. By design, this role has full access to your project's data. It also has the BYPASSRLS attribute, so it skips every Row Level Security policy you attach.
Secret keys improve on the old JWT-based service_role key, and we recommend them wherever possible. They add checks that prevent misuse:
- A secret key doesn't work in a browser. Supabase matches on the
User-Agentheader and returns HTTP 401 Unauthorized. - A project doesn't need any secret keys if nothing uses them.
Best practices for handling secret keys#
Follow these guidelines when you work with secret keys:
- Always work with secret keys on computers you fully own or control.
- Share keys through an encrypted transfer tool, such as the one in your password manager. Better still, have the other person read the key from the Settings > API Keys section of the Dashboard.
- Encrypt keys stored in files or environment variables.
- Keep keys out of source control, including CI scripts. Use the tool's own secrets storage instead.
- Use a separate secret key for each backend component. If one component leaks its key, you only rotate that one.
- Delete a leaked key immediately. The browser block returns HTTP 401 Unauthorized, but an attacker can still use the key from other tools.
- Log no more than 6 characters if you must record a key, counted from the random part after its prefix.
- Store a SHA256 hash if you need to record which key was used.
What to do if a secret key or service_role has been leaked or compromised?#
Don't rush. Fix the root cause of the leak first, before you rotate anything. The OWASP Risk Rating Methodology helps you judge the severity of the incident and plan your next steps.
To rotate a secret key, create a new one in the Settings > API Keys section of the Dashboard, then replace the compromised key with the new one everywhere your application uses it. Once every component uses the new key, delete the compromised one.
Deleting a secret key is irreversible.
If you still use the JWT-based service_role key, replace it with a new secret key. Follow the same steps as for rotating a secret key.
Known limitations and compatibility differences#
Publishable and secret keys aren't JWTs, which creates a few compatibility differences to plan for:
- You can't send a publishable or secret key in the
Authorization: Bearer ...header unless the value exactly matches theapikeyheader. Supabase forwards that request to your project's database, which rejects it because the value isn't a JWT. - Edge Functions only support JWT verification through the
anonandservice_roleJWT-based API keys. Use the--no-verify-jwtoption with publishable and secret keys. The Supabase platform doesn't verify theapikeyheader for Edge Functions called this way, so implement your ownapikeyauthorization inside the function. - Public Realtime connections last a maximum of 24 hours, unless the connection is upgraded to user-level authentication through Supabase Auth or a supported third-party auth provider.