How to disable sign-ups in Supabase for a private app (hiding the button isn't enough)
Short answer: turn off Allow new users to sign up in your Supabase project's Auth settings, then invite the people who should get in. Deleting the sign-up page from your Lovable, Bolt or Cursor app doesn't do it: your Supabase URL and public key are in your site's JavaScript, so anyone can call Supabase Auth directly. And keep Row Level Security on every table, because sign-ups off doesn't lock your data. To see what your own live site gives away, run a free SniffPup scan (SniffPup is a free passive security scanner for vibe-coded apps). Only check sites and projects you own.
Home › Guides › Disable Supabase sign-ups · Last updated · By Ember, SniffPup
The fast way: a free SniffPup scan runs 15 checks from the outside, including Supabase read access using the public config your site already ships. No signup. Ember can tell if your tables answer strangers, but she can't see your Auth settings, so do the steps below too.
A 100 means no issues were found in our checks. It is never proof of security.
1. Why a hidden sign-up page still lets strangers in
A Supabase app talks to Supabase straight from the browser, using your project URL and the public key (the legacy anon key or the newer sb_publishable_ key). Both are meant to be public, per the API keys docs, which also means anyone can copy them out of your JavaScript. With those two values, a stranger can call supabase.auth.signUp() from their own script. Your app never showed them a form; Supabase never asked your app. Think of it as taking the doorbell off but leaving the door unlocked.
2. Turn off "Allow new users to sign up"
Supabase's general Auth configuration lists Allow new users to sign up: "If this config is disabled, only existing users can sign in." Find it in your project's Authentication settings in the Supabase dashboard and switch it off. On the same page, check Allow anonymous sign-ins is off too (more on that below).
If you run Supabase locally with the CLI, the matching line in supabase/config.toml is enable_signup = false under [auth].
3. Add people by invite instead
Invite users from the Users page in the dashboard, or from your server with auth.admin.inviteUserByEmail:
// server only: needs the secret / service_role key
const { data, error } = await supabase.auth.admin.inviteUserByEmail("teammate@yourco.com");Admin methods need the secret key (sb_secret_ or legacy service_role), which bypasses RLS. Never put it in a VITE_ or NEXT_PUBLIC_ variable. Run invites from an Edge Function or server route that first checks the caller is one of your admins.
4. Keep RLS on anyway
Turning off sign-ups is a lock on the front door. Row Level Security is the lock on every room. You need both, for two reasons:
- No sign-in needed to read an open table. The public key runs as the
anonrole. If a table inpublichas RLS off and grants toanon, anyone can read it without an account. See the Supabase RLS guide. - Every signed-in user looks the same to a lazy policy. Invited users, and anonymous users if that setting is on, all get the
authenticatedrole. A policy likeusing (true)forauthenticatedlets any of them read everything.
For an internal tool, a simple allow-list works well. Keep it in a table only your server writes:
create table public.members (user_id uuid primary key references auth.users on delete cascade); alter table public.members enable row level security; -- no policies: the API can't read or edit it create policy "Members can read projects" on public.projects for select to authenticated using ( exists (select 1 from public.members m where m.user_id = (select auth.uid())) );
Never base access on user_metadata: users can edit their own. For an app with anonymous sign-ins on, check (auth.jwt()->>'is_anonymous')::boolean as Supabase's anonymous sign-in docs show.
5. Check on the server too
Anything that costs money, sends email, or touches other users (AI calls, payments, admin pages) belongs in an Edge Function or server route that checks the user's session and membership before doing anything. Hiding a button in React doesn't count as a check.
6. Test it like a stranger
With your own project URL and public key, try to sign up from outside your app. With sign-ups off, this should return an error, not a new user:
curl -s -X POST "https://YOUR-PROJECT.supabase.co/auth/v1/signup" \
-H "apikey: YOUR_PUBLIC_KEY" -H "Content-Type: application/json" \
-d '{"email":"stranger-test@yourco.com","password":"a-long-test-password-123"}'Then confirm a signed-out request to a private table returns no rows:
curl -s "https://YOUR-PROJECT.supabase.co/rest/v1/projects?select=*" \ -H "apikey: YOUR_PUBLIC_KEY"
If the first one created a user, delete it in the dashboard and recheck the setting. More on tables: Supabase RLS policy examples and is the Supabase anon key safe?
FAQ
How do I stop people signing up to my Supabase app?
In your project's Auth settings, turn off Allow new users to sign up. Supabase's docs say that with it disabled, only existing users can sign in. Then add people yourself, for example with an invite from the dashboard or auth.admin.inviteUserByEmail on your server.
I removed the sign-up page. Isn't that enough?
No. Your Supabase URL and public key ship in your site's JavaScript, and anyone can use them to call Supabase Auth's sign-up endpoint directly, page or no page. Only the Auth setting stops it.
If sign-ups are off, can I skip Row Level Security?
No. The public (anon or publishable) key still reaches every table the anon role has grants on, without signing in at all. And every signed-in user, invited or anonymous, uses the authenticated role, so policies should check who the user is, not just that they're signed in.
What about anonymous sign-ins?
Allow anonymous sign-ins is a separate setting. Anonymous users get the authenticated role too, so leave it off for a private app, or make your policies check the is_anonymous claim.
Is it safe to call inviteUserByEmail from the browser?
No. Admin methods need the secret (service_role) key, which bypasses RLS and must never ship to the browser. Call them from a server route or Edge Function that checks the caller is an admin.
Does a clean SniffPup report mean my app is private?
No. A score of 100 means no issues were found in our checks. It is never proof of security: an outside scan can't see your Auth settings, so run the signed-out test on this page too.
Sources: Supabase Auth general configuration, inviteUserByEmail, Anonymous sign-ins, Row Level Security and API keys docs. Checked October 30, 2026.
A score of 100 means no issues were found in our checks. It is not proof that your app is secure.