A leaked Supabase JWT secret is the worst-case Supabase exposure, because it is the master signing key. In the legacy model it signs every authentication token, and your anon and service_role keys are themselves tokens derived from it. Anyone with it can forge any token, impersonating any user or minting a service_role token that gives full, RLS-bypassing access to your database. Rotate the JWT secret immediately, which regenerates your API keys and invalidates every existing session, then migrate to asymmetric signing keys so a single shared secret cannot cause this again.
Short answer
The JWT secret signs everything, so a leak is total compromise. In Supabase's legacy model the JWT secret signs all auth tokens, and your anon and service_role keys are JWTs derived from it, per Supabase's API keys documentation. With it, an attacker can forge a token for any user or with the service_role claim, bypassing Row Level Security entirely. Rotate the JWT secret now, following Supabase's rotation guide; this regenerates the anon and service_role keys and invalidates all existing tokens and sessions. Update every client and server with the new keys, remove the secret from your code and Git history, and migrate to asymmetric signing keys for the long term.
What the JWT secret is
The JWT secret is the single symmetric key Supabase uses, in the legacy model, to sign and verify the JSON Web Tokens that represent every session and every API key. When a user logs in, Supabase issues a JWT signed with this secret; when a request arrives, Supabase verifies the signature with the same secret. Because it is symmetric, the same value both creates and validates tokens.
This is what makes it the master secret. Your anon and service_role keys are not separate credentials in the legacy model; they are long-lived JWTs signed by this very secret, which is why rotating the JWT secret regenerates them. Anyone who holds the JWT secret can produce a token that Supabase will accept as genuine, which is the entire trust model of your backend in one value.
What an attacker can do with it
With the JWT secret, an attacker can forge any token they want, and Supabase will trust it. They can mint a token that claims to be any user, reading and writing that user's data, or a token carrying the service_role claim, which bypasses Row Level Security and grants full access to your entire database. There is no higher level of compromise; it is complete.
The table below places the JWT secret against the other credentials, so the severity is clear.
| Secret | What it does | If leaked |
|---|---|---|
| anon / publishable key | Public client key, governed by RLS | Safe if RLS is on |
| service_role / secret key | Bypasses RLS, admin access | Full database access; rotate |
| JWT secret (legacy) | Signs all tokens and derives both keys | Forge any token; rotate everything |
Unlike a leaked anon key, which is only dangerous without RLS, and even a leaked service_role key, which RLS at least frames, a leaked JWT secret defeats everything, because it lets the attacker forge the very tokens your access control trusts. That is why it is the one to treat as a full breach the moment you suspect it.
Step 1: rotate the JWT secret immediately
Rotating is the only action that neutralizes a leaked JWT secret, so do it first. In your Supabase project, roll the JWT secret from the API settings, which generates a new secret and re-signs your keys. This is the single most important step, because until the secret is changed, every forged token the attacker made remains valid.
Do it before anything else, and accept the disruption. Rotating the JWT secret is intentionally heavy precisely because the secret is so central; that weight is the point, since it is what invalidates the attacker's forged tokens along with everyone else's. Treat the leaked secret as compromised even if you rotate within minutes, and review your logs for signs of forged-token access.
What rotation invalidates
Rotating the JWT secret invalidates every token signed with the old one, which has real, immediate effects you should expect. All existing user sessions become invalid, so every user is effectively signed out and must log in again. Any cached or stored tokens stop working, and any service using an old token fails until updated. This is not a bug; it is the mechanism that locks the attacker out.
Because the anon and service_role keys are derived from the secret, they change too. Every place that used the old anon or service_role key, your web client, your mobile app, your Edge Functions, your CI, will stop working until you update it with the new key. Plan for this: the rotation that saves you also briefly breaks your own app, so line up the key updates to follow immediately.
Update every client and server with the new keys
After rotation, propagate the new keys everywhere the old ones lived. Update the anon key in your frontend and mobile app, update the service_role key in your backend and Edge Functions, and update any keys stored in CI secrets or configuration. Miss one and that part of your system stays broken, so treat this as a checklist of every place a Supabase key appears.
This is also the moment to fix any exposure that caused the leak. If the JWT secret or a service_role key was in your client or repository, do not simply paste the new one into the same place; move secrets server-side and keep only the public anon key in the client. Otherwise you will rotate straight into a second leak.
Migrate to asymmetric JWT signing keys
The durable fix is to stop relying on a single shared symmetric secret at all. Supabase now offers asymmetric JWT signing keys, where a private key signs tokens and a public key verifies them, and the private key never leaves Supabase. Because verification uses only the public key, there is no shared secret for your code to leak, and you can rotate keys without the all-or-nothing disruption of the legacy secret.
Migrating to signing keys is the way to make sure this incident cannot repeat in the same form. It removes the master symmetric secret from the picture, supports smoother rotation, and aligns with how modern token systems work. After you have contained the current leak, plan the migration as the structural improvement that prevents the next one.
RLS does not save you here
It is worth being clear that Row Level Security, which protects you from an exposed anon key, does not protect you from a leaked JWT secret. RLS enforces policies based on the identity in a token, but a JWT secret lets an attacker forge a token with any identity, including one that bypasses RLS entirely. The control assumes tokens are genuine, and the leaked secret breaks that assumption.
This is why the JWT secret sits above RLS in severity. RLS remains essential as defense in depth, and you should have it enabled on every table regardless, but for this specific leak the fix is rotation and migration, not policy. Do not treat enabling RLS as a substitute for rotating the secret; it is a complement, not a cure.
Verification checklist
After you respond, verify each part rather than assuming. The checklist below confirms the leak is closed.
| Step | Action | What it confirms |
|---|---|---|
| 1 | Rotate the JWT secret in Supabase | Old signatures are invalid |
| 2 | Confirm old tokens and sessions are rejected | Forged tokens no longer work |
| 3 | Update clients and servers with the new keys | Your apps work again |
| 4 | Remove the secret from code and Git history | No re-leak of the old secret |
| 5 | Enable RLS on every table | Defense in depth is in place |
| 6 | Plan migration to signing keys | The long-term fix is scheduled |
The verification that matters most is confirming that old tokens are rejected: try an old session or token and ensure Supabase refuses it. If forged and stale tokens no longer work, your new keys are in place, and the secret is out of your code and history, the immediate incident is contained, and the signing-key migration is what keeps it contained.
Prevent the next leak: scan and secret hygiene
Most JWT secret leaks are avoidable accidents, a secret committed to a repository or shipped in a build, so preventing the next one is about secret hygiene and checking what you ship. Keep the JWT secret and service_role key server-side only, never in client code or Git, enable GitHub secret scanning and push protection, and scan your build before release.
A scanner like PTKD.com analyzes your mobile build and returns findings ordered by severity and mapped to OWASP MASVS, including embedded secrets, so a secret does not reach users in the first place. To be clear about the boundary: a build scanner checks the app you ship, not your Supabase project, so it does not rotate the secret or migrate you to signing keys. It catches the client-side exposure that starts many of these incidents; the server-side response is still yours to run.
What to take away
- The Supabase JWT secret is the master signing key; a leak lets an attacker forge any token, including service_role.
- Rotate the JWT secret immediately, which regenerates your anon and service_role keys and invalidates all sessions.
- Expect rotation to log every user out and break clients until you update them with the new keys.
- RLS does not protect you from a forged token, so rotation, not policy, is the fix for this leak.
- Migrate to asymmetric signing keys to remove the shared secret, and scan each build with PTKD.com to prevent the next leak.



