
Hemant Bhatt
October 16, 2025
Unleashing Server-Side Superpowers: Mastering Supabase's elevated previlege key

Introduction
Your Supabase project needs two types of API keys: the public-facing keys (anon
or publishable
) that play by Row Level Security rules, and the admin keys (service_role
or secret
) which are like VIP access pass that bypasses every velvet rope. One's for your users; the other's for your server when it needs to get things done without asking permission.
API Keys Evolution: Legacy vs. New Format
Supabase has introduced a new, more secure API key format while maintaining backward compatibility:
Legacy Format
anon
- Public, client-safeservice_role
- Admin privileges
New Format
publishable_key
- Public, client-safesecret_key
- Admin privileges
Both formats work identically and provide the same level of access. The new secret_key
key offer enhanced security with the ability to create multiple secret keys and revoke them individually—perfect for managing different services or rotating keys.
Why You Need Admin API Keys
Admin API keys (either the legacy service_role
or new secret
keys) grant administrative privileges across your Supabase project:
- Database Operations: Complete table access, bypassing Row Level Security
- Storage Management: Full control over buckets and files, regardless of policies
- Auth Administration: Create users, update emails, manage sessions, and perform admin-only operations
When to Use It
Use admin keys (service role or secret) exclusively in server-side contexts:
- Server Actions
- API Routes (
app/api/...
) - Server Components
- Backend scripts and migrations
- Scheduled jobs and webhooks
Setting Up Your Admin API Key
Step 1: Choose Your API Key Format
In your Supabase project dashboard, navigate to Settings → API Keys. You'll see two tabs:
- Legacy API Keys :
anon
/public
- Safe for client-side useservice_role
- Your admin key
- API Keys (New format with enhanced security):
Publishable key
- Safe for client-side useSecret keys
- Admin privileges, can create multiple and revoke individually
Which should you use?
Both formats work identically and provide the same admin privileges. However, the new secret keys offer advantages:
- Create multiple secret keys for different services
- Revoke individual keys without affecting others
- Better key rotation and management
- Future-proof as Supabase evolves
Recommendation: Use the new secret keys for new projects. Existing projects can continue using service_role keys or migrate gradually.
Step 2: Store It Securely in Environment Variables
Create or update .env.local
in your Next.js project root. You can use either the legacy or new key format:
# Public keys - safe for client
NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY=your-anon-key-here
# Option 1: Legacy service_role key
SUPABASE_SERVICE_ROLE_KEY=your-service-role-key-here
# Option 2: New secret key (recommended for new projects)
# SUPABASE_SECRET_KEY=sb_secret_...
Critical Security Warning:
Variables prefixed with NEXT_PUBLIC_
are exposed to the browser. Your admin keys (whether service_role
or secret
) must NEVER EVER have this prefix. Both formats bypass Row Level Security and provide full database access.
Step 3: Create a Server-Side Supabase Client
Create a utility file for your admin client. This example shows both key formats:
1 import "server-only"; 2 import { createClient } from "@supabase/supabase-js"; 3 4 const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; 5 6 // Use either the legacy service_role key or the new secret key 7 // Both provide identical admin privileges 8 const supabaseAdminKey = 9 process.env.SUPABASE_SECRET_KEY || // New format (recommended) 10 process.env.SUPABASE_SERVICE_ROLE_KEY!; // Legacy format (still works) 11 12 export const supabaseAdmin = createClient(supabaseUrl, supabaseAdminKey, { 13 auth: { 14 autoRefreshToken: false, 15 persistSession: false, 16 }, 17 });
This creates a dedicated admin client for server-side code only. The server-only
import ensures this file cannot accidentally be imported in client components.
Practical Examples
Example 1: Server Action - Deleting Users Programmatically
Remove users from your system through an admin panel using a Server Action:
1 "use server"; 2 3 import { supabaseAdmin } from "@/lib/supabase-admin"; 4 import { getCurrentUser } from "@/lib/auth"; 5 6 export async function deleteUserAsAdmin(userId: string) { 7 // Check if the current user is an admin 8 const currentUser = await getCurrentUser(); 9 10 if (!currentUser?.is_admin) { 11 return { success: false, error: "Unauthorized: Admin access required" }; 12 } 13 14 // Perform the admin operation 15 const { data, error } = await supabaseAdmin.auth.admin.deleteUser(userId); 16 17 if (error) { 18 return { success: false, error: error.message }; 19 } 20 21 return { success: true, message: "User deleted successfully" }; 22 }
Key insight:
Always verify authorization before executing admin operations. The.auth.admin
API bypasses RLS whether you use the legacyservice_role
key or new secret
keys, but you must implement your own permission checks.Example 2: Storage Management - List All Files in a Bucket
List all files and folders within a bucket path, bypassing storage policies:
1 "use server"; 2 3 import { supabaseAdmin } from "@/lib/supabase-admin"; 4 import { getCurrentUser } from "@/lib/auth"; 5 6 export async function listBucketFiles(bucketName: string, folderPath: string) { 7 // Check if the current user is an admin 8 const currentUser = await getCurrentUser(); 9 10 if (!currentUser?.is_admin) { 11 return { success: false, error: "Unauthorized: Admin access required" }; 12 } 13 14 // List all files in the bucket, ignoring storage policies 15 const { data, error } = await supabaseAdmin.storage 16 .from(bucketName) 17 .list(folderPath, { 18 limit: 100, 19 offset: 0, 20 sortBy: { column: 'name', order: 'asc' }, 21 }); 22 23 if (error) { 24 return { success: false, error: error.message }; 25 } 26 27 return { success: true, files: data }; 28 }
For the full list of available Supabase admin operations, check out the official Supabase JavaScript documentation.
Common Pitfalls and How to Avoid Them
Pitfall #1: Using Admin Keys in Client Components
The Problem: Accidentally importing the admin client in a file with 'use client'
at the top exposes your admin key to the browser.
The Fix: Double-check your imports. Mark server modules or utilities with import 'server-only'
to prevent accidental client-side usage. Read more about nextjs server patterns.
Pitfall #2: Skipping Authorization Checks
The Problem: Assuming that because your Server Action uses an admin key, it's automatically secure. Server action can be called by anyone.
The Fix: Always implement authorization checks before running admin logic. Verify the user's identity and permissions before executing admin operations.
Conclusion
Supabase admin API keys unlock server-side administrative capabilities in Next.js. Whether you use the legacy service_role
JWT or the new secret
key format, they enable operations that transcend user-level permissions, from managing authentication to bypassing RLS for bulk operations.
Key takeaways:
- Choose your format: Legacy
service_role
keys still work perfectly, but newsecret
keys offer better management. - Use only in server-side contexts (Server Actions, API Routes, Server Components).
- Never expose to the client—both formats bypass RLS and provide full access
You're now equipped to build secure, powerful admin features. Remember: with great admin keys comes great responsibility (and hopefully, good backups). 🚀