Most builders ship apps with zero security.
They focus on features, design, and shipping fast. Security feels like homework. It's boring. It's not sexy. It doesn't get quote-tweets.
Then they hit 10 users and the app starts breaking. Or worse, someone opens the browser console and realizes they can see the entire database.
This is the exact 30-minute security checklist I run before every MVP launch. It's not exhaustive. It's not paranoid. It's the minimum viable security layer that protects you from the most common attacks and keeps your app from leaking data or racking up surprise bills.
If you're shipping AI tools, SaaS, or any app with user data, bookmark this and run through it before you publish.
1. Row Level Security in Supabase
This is the number one thing people skip and it's deadly.
Without Row Level Security, anyone can read your entire database by opening the browser console and running a query. They don't need to hack anything. They don't need special tools. They just open DevTools and type a command.
I've seen apps with thousands of users ship without RLS enabled. The database is wide open. User emails, passwords (hopefully hashed), payment data, everything.
Here's how you check:
Go to your Supabase dashboard. Click Authentication, then Policies. If you see zero policies, your app is completely exposed.
The fix is simple. You need to add policies that restrict who can read, insert, update, or delete rows based on the authenticated user.
If you're using Lovable, just ask it to enable RLS and write policies for your tables. It'll generate the SQL and apply it automatically.
If you're doing it manually, here's the basic structure:
Create a policy that says "users can only read rows where the user_id matches their own ID." Do this for every table that stores user-specific data.
This takes 5 minutes and it's the difference between a secure app and a data breach waiting to happen.
Don't skip this. Ever.
2. Test every single auth flow
Signup, login, password reset, email verification.
Most devs only test the happy path. They sign up with a valid email, log in, and call it done.
But apps break when things go wrong. What happens when someone enters the wrong password five times? What happens when they try to reset a password for an email that doesn't exist? What happens when they click the verification link twice?
You need to test the failure cases because that's where attackers probe first.
Here's what I test:
Login with wrong password. Does it show a generic error or does it say "password incorrect" (which tells attackers the email exists)?
Password reset for a non-existent email. Does it reveal whether the email is in the system?
Email verification link clicked twice. Does it error gracefully or break the flow?
Signup with an email that already exists. Does it handle it properly or leak information?
This takes 10 minutes and catches 80% of auth vulnerabilities before they go live.
3. Rate limits on API endpoints
Without rate limits, someone can spam your API 10,000 times in a minute.
Maybe they're trying to brute force a login. Maybe they're scraping your database. Maybe they're just being malicious. Either way, you're paying for it.
I've seen Supabase bills jump from $20 to $200 in a day because someone found an unprotected endpoint and hammered it with requests.
Rate limiting is insurance. It caps how many requests a single IP or user can make in a given time window.
If you're using Supabase Edge Functions, you can add rate limiting with Upstash.
A basic rate limit looks like this: 100 requests per minute per IP for public endpoints. 1,000 requests per minute for authenticated users.
This takes 5 minutes to set up and saves you hundreds in surprise bills.
4. Server-side validation on every form
Never trust what the frontend sends you.
Even if you have validation in React, even if you're using Zod or Yup or any schema validation library on the client, you need to validate again on the server.
Attackers bypass frontend validation in seconds. They open the browser console, disable JavaScript, or use Postman to send requests directly to your API.
If your only validation is on the frontend, they can send whatever they want. Malformed data. SQL injection attempts. Scripts. Anything.
Server-side validation is your last line of defense.
Here's the rule: if a form submits data to your database, validate it in your Edge Function or API route before you write it.
Check data types. Check length limits. Check for SQL injection patterns. Sanitize inputs.
This is not optional. This is the baseline.
5. Lock down environment variables
Your API keys should never be in your frontend code.
I've seen apps where the OpenAI API key is hardcoded in a React component. I've seen Stripe secret keys committed to GitHub repos. I've seen Supabase service role keys sitting in .env files that got deployed to production.
Once an API key is public, it's compromised forever. You can't take it back. You have to regenerate it and update every service that uses it.
Here's the rule:
Public keys (like Supabase anon keys) can go in the frontend. Secret keys (service role, Stripe secret, OpenAI) must stay server-side.
Store secrets in Supabase Edge Function Secrets or Vercel environment variables. Never commit them to version control.
If you're using Lovable, ask it to add environment variables securely. It'll handle the setup automatically.
If you think a key might have been exposed, regenerate it immediately. Don't wait. Don't hope nobody found it.
6. Add CAPTCHA to public forms
Contact forms, signup pages, anything public facing.
Without CAPTCHA, bots will spam you into oblivion.
I've seen contact forms get 500 spam submissions in an hour. I've seen signup pages flooded with fake accounts. It's not a question of if, it's when.
CAPTCHA is cheap, easy, and effective.
Use Cloudflare Turnstile (free and privacy-focused) or Google reCAPTCHA. Integration takes 10 minutes.
Add it to any form that's publicly accessible and doesn't require authentication. Signup, contact, waitlist, anything.
You'll forget about bot attacks completely.
7. Enable CORS restrictions
If your API is open to any domain, attackers can call it from their own sites.
CORS (Cross-Origin Resource Sharing) controls which domains are allowed to make requests to your API.
By default, many frameworks allow requests from anywhere. That's fine for local development. It's a disaster in production.
Here's the fix:
In your Supabase Edge Functions or API routes, specify exactly which domains can access your API.
Allow your production domain. Allow localhost for testing. Block everything else. This takes 2 minutes and prevents cross-site request forgery and unauthorized API access.
8. Error handling that doesn't leak data
Your error messages should not show database table names, SQL queries, or internal logic.
I've seen apps that return errors like "SELECT * FROM users WHERE email = 'test@test.com' failed." That tells an attacker your table structure, your column names, and your query logic. Good error message: "User not found."
Bad error message: "Database query failed: no rows in table 'users' matched."
Log errors server-side with full context so you can debug them. Show generic messages to users.
This is basic operational security and it prevents attackers from learning how your system works.
9. Run code reviews before publishing
Before you commit anything to GitHub, run a security check.
Most modern AI tools have this built in now. Cursor, Claude, and visual builders like Lovable all include security scanners that catch common issues before they hit production.
They'll flag:
→ RLS misconfigurations → Exposed secrets in your code → Vulnerable dependencies → Insecure patterns Fix everything they flag. Don't ship with warnings. Don't tell yourself you'll fix it later. Security debt compounds fast.
Here's everything in one place:
- Row Level Security enabled in Supabase
- Auth flows tested (signup, login, password reset, edge cases)
- Rate limits on all API endpoints
- Server-side validation on every form
- Environment variables locked down (no secrets in frontend)
- CAPTCHA on public forms
- CORS restrictions enabled
- Error handling that doesn't leak data
- Security scan completed and warnings fixed
This checklist takes 30 minutes.
It's the difference between shipping an app that scales and shipping an app that leaks user data or racks up surprise bills.
Most vibe coders skip this because it's not exciting. It doesn't feel like progress. It's not something you can show in a demo.
But it's the difference between an MVP and a liability.
Security isn't optional anymore. Users expect it. Platforms enforce it. Investors check for it.
You don't need to be paranoid. You don't need enterprise-grade security on day one.
You just need this checklist.
Run it before every launch. Make it part of your workflow. Treat it like testing or deployment.
Because the apps that survive aren't just the ones that ship fast. They're the ones that ship fast and don't break when real users show up.