lucid.page Supabase RLS on Next.js App Router: 7 checks before you ship
Text size
Read time2 min

# Supabase RLS on Next.js App Router: 7 checks before you ship

Shipping a Next.js app with Supabase auth is easy until row level security silently fails. Use this checklist before you call a deploy done.

Contents

# 1. Every table that holds user data has RLS on

In the Supabase SQL editor, confirm relrowsecurity is true for profiles, projects, uploads, and any table your app reads with the anon or authenticated key. A single forgotten table is a data leak.

# 2. Policies name auth.uid(), not a client-supplied user id

Policies that trust user_id = request.headers->>'x-user-id' can be forged. Prefer auth.uid() = user_id (or a join through a membership table).

If you create a Supabase client with only the anon key and no request cookies, Server Components see a logged-out user. Use the official @supabase/ssr helpers so the session rides on cookies across Middleware, Server Components, and Route Handlers.

# 4. Middleware refreshes the session on every HTML navigation

Without a refresh, expired access tokens look like random logouts. Keep the middleware matcher broad enough for app pages, and skip only static assets.

# 5. Service role keys never ship to the browser

SUPABASE_SERVICE_ROLE_KEY belongs in server-only env (Vercel server env, not NEXT_PUBLIC_). Grep the client bundle for service_role before release.

# 6. Storage buckets are not public by default

A public bucket with predictable object names exposes uploads. Prefer private buckets plus signed URLs, and mirror ownership checks in storage policies.

# 7. Write one failing test that proves RLS

Insert a row as user A, then select it with user B’s JWT. If B can read it, do not ship.

Built with these patterns in mind: Massvai is an AI app builder that ships full-stack Next.js, Supabase and Vercel apps so founders spend less time wiring auth and more time on the product.

End