Bolt.new + Supabase Security Blueprint

To secure a Bolt.new + Supabase stack, start by confirming RLS is actually enabled on every table you exported. Then check that client code only ever holds the anon key, that your session survives a page refresh, and that queries filter by user ID even with RLS on. Four checks, and the first one is the one that matters most.

Setup Time1-2 hours

TL;DR

Bolt builds a working full-stack app fast. What it tends to leave behind is the Supabase security config: RLS disabled or written so permissively it may as well be, and sometimes a service_role key sitting in code the browser downloads. Fix those first. Session handling and per-query auth checks come after.

Bolt.new Security Considerations

The split is fairly consistent. Bolt gets the plumbing right and leaves the policy decisions to you:

What Bolt Does WellWhat Needs Review
Generates working Supabase client setupRLS policies may be missing or permissive
Creates authentication UISession handling may be incomplete
Scaffolds CRUD operationsAuthorization checks often missing
Sets up environment variablesMay expose service_role key

Part 1: Check Supabase RLS Configuration

Verify RLS is Enabled

This is the single highest-value query in the whole blueprint. Run it in the Supabase SQL editor before anything else:

SQL to check RLS status
-- Run in Supabase SQL Editor
SELECT
  schemaname,
  tablename,
  rowsecurity
FROM pg_tables
WHERE schemaname = 'public';

-- If rowsecurity is 'false', enable it:
ALTER TABLE your_table ENABLE ROW LEVEL SECURITY;

Add Proper RLS Policies

Common RLS policies for Bolt apps
-- User profiles: only owner can access
CREATE POLICY "Users can view own profile"
  ON profiles FOR SELECT
  USING (auth.uid() = id);

CREATE POLICY "Users can update own profile"
  ON profiles FOR UPDATE
  USING (auth.uid() = id);

-- User's items: full access to own data
CREATE POLICY "Users can manage own items"
  ON items FOR ALL
  USING (auth.uid() = user_id);

-- Public read, authenticated write
CREATE POLICY "Anyone can read posts"
  ON posts FOR SELECT
  USING (true);

CREATE POLICY "Authenticated users can create posts"
  ON posts FOR INSERT
  WITH CHECK (auth.uid() = author_id);

Watch for the permissive default. Bolt commonly writes a policy equivalent to allow read, write: if true, or skips RLS on the table entirely. Both look fine in the dashboard. Neither protects anything.

Part 2: Supabase API Key Security

Check Key Usage in Generated Code

The service_role key bypasses RLS completely, so one copy of it in client code undoes everything in Part 1. Grep for it:

Search for potential key exposure
# In your exported project directory
grep -r "service_role" .
grep -r "supabase" . --include="*.ts" --include="*.js"

# Look for hardcoded keys
grep -r "eyJ" . --include="*.ts" --include="*.js"

Correct Key Setup

lib/supabase.ts - Safe client setup
import { createClient } from '@supabase/supabase-js'

// Only use anon key in client code
const supabaseUrl = import.meta.env.VITE_SUPABASE_URL
const supabaseAnonKey = import.meta.env.VITE_SUPABASE_ANON_KEY

export const supabase = createClient(supabaseUrl, supabaseAnonKey)

// NEVER do this in client code:
// const supabase = createClient(url, process.env.SUPABASE_SERVICE_ROLE_KEY)

Part 3: Authentication Flow

Verify Session Handling

The usual symptom is being logged out on every refresh, because the app reads the session once and never subscribes to changes. Here's the shape that works:

hooks/useAuth.ts - Proper session handling
import { useEffect, useState } from 'react'
import { supabase } from '../lib/supabase'
import { User, Session } from '@supabase/supabase-js'

export function useAuth() {
  const [user, setUser] = useState<User | null>(null)
  const [loading, setLoading] = useState(true)

  useEffect(() => {
    // Get initial session
    supabase.auth.getSession().then(({ data: { session } }) => {
      setUser(session?.user ?? null)
      setLoading(false)
    })

    // Listen for auth changes
    const { data: { subscription } } = supabase.auth.onAuthStateChange(
      (_event, session) => {
        setUser(session?.user ?? null)
      }
    )

    return () => subscription.unsubscribe()
  }, [])

  return { user, loading }
}

Part 4: Data Fetching Security

Add Authorization to Queries

Generated queries tend to select everything and trust RLS to sort it out. RLS should, but a filter costs you nothing and catches the case where a policy was never written. This is the pattern we see fail most often in exported Bolt apps:

BEFORE: Bolt-generated query (potentially unsafe)
// May fetch all items regardless of owner
const { data } = await supabase
  .from('items')
  .select('*')
AFTER: Scoped to current user
// With proper RLS, this returns only user's items
const { data: { user } } = await supabase.auth.getUser()

if (!user) {
  throw new Error('Not authenticated')
}

const { data } = await supabase
  .from('items')
  .select('*')
  .eq('user_id', user.id)  // Explicit filter (defense in depth)

Security Checklist

Post-Export Checklist for Bolt + Supabase

RLS enabled on all tables

RLS policies restrict access properly

Only anon key used in client code

Service role key not in repository

Auth state properly persisted

Protected routes check authentication

Environment variables not hardcoded

No sensitive data in git history

Alternative Stacks to Consider

      **Bolt.new + Firebase**
      If you prefer Firebase's ecosystem


      **Bolt.new + Supabase + Vercel**
      Add Vercel deployment security


      **Bolt.new + Convex**
      Real-time alternative with built-in functions

Can I trust Bolt-generated Supabase code?

Trust it to run. Don't trust it to be locked down. The code works, but the security configuration is left to you, so check RLS policies, key usage, and auth flows before anything goes to production.

Why does my Bolt app work without RLS?

Because without RLS the anon key reads and writes everything, which is exactly why the app works. That's convenient while you're building and a full data breach once you're live. Turn RLS on and write the policies before you ship.

How do I add server-side code to a Bolt app?

Export the project, then add API routes or server functions on whatever platform you deploy to. That's the only place the service_role key belongs, and only for genuine admin operations.

Exported a Bolt + Supabase app?

Scan for RLS misconfigurations and key exposure.

Security Blueprints

Bolt.new + Supabase Security Blueprint