Everything you need to secure your Supabase application. RLS policies, auth configuration, API key management, and common pitfalls to avoid.
Run this in your Supabase SQL editor to check if RLS is enabled on your tables:
SELECT schemaname, tablename, rowsecurity
FROM pg_tables
WHERE schemaname = 'public';
-- All tables should show rowsecurity = trueSupabase security is built on three pillars:
Database-level policies that control who can read/write each row
User identity management with JWT tokens
Anon key (public) vs Service key (private)
The Supabase anon key is not a secret. It's designed to be exposed in your frontend. Security comes from RLS policies, not from hiding this key.
RLS must be explicitly enabled on each table. Without it, anyone with the anon key can read all data.
-- Enable RLS on a table
ALTER TABLE "posts" ENABLE ROW LEVEL SECURITY;
-- Verify RLS is enabled
SELECT tablename, rowsecurity
FROM pg_tables
WHERE tablename = 'posts';-- Re-evaluates for every row
CREATE POLICY "Users see own"
ON posts FOR SELECT
USING (auth.uid() = user_id);-- Evaluates once per query
CREATE POLICY "Users see own"
ON posts FOR SELECT
USING ((select auth.uid()) = user_id);Wrapping auth.uid() in a SELECT caches the result, significantly improving query performance.
CREATE POLICY "Users CRUD own data" ON "todos"
FOR ALL TO authenticated
USING ((select auth.uid()) = user_id)
WITH CHECK ((select auth.uid()) = user_id);CREATE POLICY "Anyone can read" ON "posts"
FOR SELECT USING (true);
CREATE POLICY "Owner can update" ON "posts"
FOR UPDATE TO authenticated
USING ((select auth.uid()) = author_id);CREATE POLICY "Team members access" ON "projects"
FOR ALL TO authenticated
USING (
EXISTS (
SELECT 1 FROM team_members
WHERE team_members.team_id = projects.team_id
AND team_members.user_id = (select auth.uid())
)
);| Key Type | Can be Public? | Respects RLS? | Use Case |
|---|---|---|---|
| anon key | Yes | Yes | Frontend client |
| service_role key | NO! | No (bypasses) | Server-side only |
The service_role key bypasses all RLS policies. If exposed, attackers have full database access.
Yes, these are designed to be public. Your security comes from RLS policies, not from hiding these values. Think of them like a read-only API endpoint that respects user permissions.
Create two test users, log in as each, and try to access each other's data. Or use Supabase's SQL editor with 'SECURITY DEFINER' vs 'SECURITY INVOKER' to test policies.
Technically yes, but you'd need to put all database calls behind authenticated API routes and never use the client directly. RLS is almost always the better choice.
Edge Functions run server-side and can safely use the service_role key. Validate inputs, use typed responses, and don't expose internal errors to clients.
Scan your Supabase application for misconfigurations, exposed keys, and RLS issues.
Scan Your App FreeLast updated: January 2025