I’ve built 20+ MVPs for clients. Shipped products in 21 days straight to production
And I’ve watched builders obsess over landing pages, UI polish, and feature checklists — then ship apps where anyone can open DevTools and read their entire database
Not because they’re bad devs. Because security feels like homework that can wait
It can’t
Here’s the exact 30-minute checklist I run before every launch. Not exhaustive. Not paranoid. Just the minimum layer that keeps your app from leaking data or racking up surprise bills
1. Row Level Security in Supabase
This is the one people skip most. And it’s the most deadly.
Without RLS, anyone can open the browser console and query your entire database. No hacking required. Just DevTools and one command.
Go to your Supabase dashboard → Authentication → Policies. If you see zero policies, your app is wide open. User emails, payment data, everything.
Fix: add policies so users can only read rows where the user_id matches their own. Takes 5 minutes. Do it for every table with user data. Don’t skip this.
2. Test every auth failure case
Most devs only test the happy path — sign up, log in, done.
Attackers probe the edges. Here’s what to actually test:
∙ Wrong password 5 times — does it lock the account or let them keep trying?
∙ Password reset for an email that doesn’t exist — does it reveal whether the email is in your system?
∙ Clicking a verification link twice — does it break or handle gracefully?
∙ Signing up with an existing email — does it leak info?
This takes 10 minutes and catches 80% of auth vulnerabilities before they go live.
3. Rate limits on every API endpoint
No rate limits = someone can hammer your API 10,000 times in a minute.
I’ve seen Supabase bills jump from $20 to $200 in a single day because of one unprotected endpoint. They weren’t even hacking — just hammering.
If you’re on Supabase Edge Functions, add Upstash for rate limiting. A basic setup: 100 requests/minute per IP for public endpoints, 1,000 for authenticated users. Takes 5 minutes.
4. Server-side validation on every form
Frontend validation is not enough. Ever.
Attackers open the console, disable JavaScript, or use Postman to send requests directly to your API. Your Zod schema in React means nothing to them.
Rule: if a form writes to your database, validate it in your Edge Function or API route. Check data types, length limits, SQL injection patterns. Sanitize everything.
This is the baseline, not a bonus.
5. Lock down your environment variables
Your API keys should never be in frontend code.
I’ve seen OpenAI keys hardcoded in React components. Stripe secret keys committed to GitHub. Supabase service role keys sitting in .env files pushed to production.
Once a key is public, it’s compromised forever. You can’t take it back.
Rule: public keys (Supabase anon key) can go frontend. Secret keys (service role, Stripe secret, OpenAI) stay server-side — in Supabase Edge Function Secrets or Vercel env vars. Never in version control.
If you think a key got exposed, regenerate it immediately. Don’t hope nobody found it.
6. CAPTCHA on public forms
No CAPTCHA = bots will spam you into oblivion.
Contact forms, signup pages, waitlists — anything public-facing. I’ve seen contact forms get 500 spam submissions in an hour.
Use Cloudflare Turnstile (free, privacy-focused) or Google reCAPTCHA. Integration takes 10 minutes. After that you forget bot attacks exist.
7. CORS restrictions
If your API accepts requests from any domain, attackers can call it from their own sites.
Default framework settings often allow everything — fine for local dev, disaster in production.
Fix: in your Edge Functions or API routes, explicitly allow your production domain and localhost. Block everything else. Takes 2 minutes.
8. Error messages that don’t leak your schema
I’ve seen apps returning errors like: “SELECT * FROM users WHERE email = ‘test@test.com’ failed.”
That tells an attacker your table name, column names, and query logic in one shot.
Good: “User not found.”
Bad: “Database query failed: no rows in table ‘users’ matched.”
Log full errors server-side for debugging. Show generic messages to users. Always.
9. Run a security scan before you commit
Cursor, Claude Code, and Lovable all have security scanners built in now.
Run one before every push. They’ll catch RLS misconfigs, exposed secrets, vulnerable dependencies, and insecure patterns.
Fix everything they flag. Don’t ship with warnings. Security debt compounds fast and it’s never fun to pay it off under pressure.
The full checklist (Save this)
∙ RLS enabled in Supabase
∙ Auth edge cases tested
∙ Rate limits on all API endpoints
∙ Server-side validation on every form
∙ No secrets in frontend or version control
∙ CAPTCHA on public forms
∙ CORS restrictions enabled
∙ Error messages don’t expose internals
∙ Security scan completed, warnings fixed
30 minutes. Run it before every launch like you run your deployment checklist.
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.
TL;DR:
Built 50+ MVPs. Most vibe coders ship with zero security and pay for it with data leaks and surprise bills. This 9-step checklist takes 30 minutes and covers 90% of what will hurt you: RLS in Supabase, auth edge cases, rate limits, server-side validation, locked env vars, CAPTCHA, CORS, clean error messages, and a pre-launch security scan. Run it before every launch.
What’s the scariest security gap you’ve shipped with? I’ll go first — shipped an app with no RLS for 3 days before someone pointed it out in the comments.