Storage RLS
Storage objects participate in the same row-level security engine as the rest of the graph. Policies attach to the storage.Object label (or the storage.objects collection) using the familiar CREATE POLICY syntax.
Standard claims
Every storage RLS predicate has these session variables in scope:
| Variable | Source |
|---|---|
| auth.uid() | JWT sub claim - current user ID |
| auth.role() | JWT role claim |
| auth.jwt() | Full JWT payload as a map |
| row | The :storage.Object being checked |
Examples
-- Only the owner can read their objects.
CREATE POLICY avatars_owner_select
ON LABEL `storage.Object`
FOR SELECT
USING (row.owner = auth.uid());
-- Only the owner can upload to their folder.
CREATE POLICY avatars_owner_insert
ON LABEL `storage.Object`
FOR INSERT
WITH CHECK (
row.owner = auth.uid()
AND row.path STARTS WITH (auth.uid() || '/')
);
-- Anyone authenticated can read; only admins can write.
CREATE POLICY storage_admin_write
ON LABEL `storage.Object`
FOR INSERT, UPDATE, DELETE
WITH CHECK ('admin' IN auth.role());Bypass for service_role
Edge functions and server-side code typically authenticate with the service_role claim, which bypasses all storage RLS (just like Postgres / Supabase). End-user requests always go through the policy gauntlet.
Public bucket vs RLS
The public-download endpoint (/storage/v1/object/public/...) is a deliberate side-channel: it bypasses RLS because the bucket's public: true flag is the authorization. Authenticated downloads through /storage/v1/object/{bucket}/{path} always consult RLS.