The short version
- Secrets: only publishable keys in the browser. Anything that can spend money or read private data stays on a server.
- Data: row-level security on every Supabase table, tested with the public key, not assumed.
- Costs: every AI or paid API route needs auth, a rate limit and a provider budget cap.
- Polish: real title, favicon, meta description and a 1200x630 share image, so the launch post looks finished.
1. Secrets and keys
The single most expensive vibe-coding mistake is a secret key shipped to the browser. Anything in your front-end code, including environment variables with a public prefix, is downloadable by every visitor.
- No secret keys in client code. Search your built JavaScript for
sk_,sk-,service_roleandwhsec_. See which env variables are safe in the browser. - AI calls go through your server. The browser calls your route, your route calls OpenAI. See how to hide your OpenAI key.
- Payments are created server-side. Only the Stripe publishable key belongs in the browser. See keeping your Stripe secret key server-side.
- Anything that ever leaked has been rotated. Deleting the line of code doesn't revoke the key. See how to rotate a leaked API key.
2. Database and storage
Supabase's public key is meant to be visible. What protects your data is row-level security (RLS) and the policies you write. With RLS off, anyone holding that public key can query your tables directly.
- RLS is on for every table in the
publicschema, with policies that match who should see what. See turning on RLS in Supabase. - You tested it with the public key. A request using only your anon or publishable key should not return other users' rows.
- Storage buckets are private unless the files are truly meant for anyone (logos, public images).
- Admin features are checked on the server, not just hidden in the UI.
Built with Lovable or Bolt? Run the 10-minute Lovable security check, which walks through each of these for Supabase-backed apps.
3. API abuse and runaway bills
A protected key can still be abused through your own endpoint. If /api/chat answers anyone, a script can call it thousands of times on your bill.
- Every API route checks who is calling. See adding auth to your API routes.
- AI routes are rate limited per user and cap input length and output tokens. See how to rate limit an AI API route.
- A monthly budget and alert is set with every paid provider (OpenAI, Anthropic, email, SMS).
4. Launch polish people actually notice
None of these are security issues, but they decide whether your launch post gets clicked and whether the app looks finished.
- Real page title, not "Vite + React" or "Create Next App", and your own favicon. See changing the default title and favicon.
- Share image (og:image) at 1200x630 with an absolute URL. See Open Graph images that don't look broken.
- Meta description that says who it's for and what it does, in one sentence.
- Mobile viewport tag present, and you've opened the site on a real phone.
5. Production hygiene
- No
console.logordebuggerin production JS. See removing console.log from production. - Source maps aren't public, unless you've decided you're fine with your source being readable. See turning off source maps.
- HTTPS everywhere, with http redirecting to https.
- A privacy policy if you collect emails or any personal data.
Check your live site in 60 seconds
ShipReady scans your deployed URL and the JavaScript visitors actually download: leaked keys, Supabase exposure, unprotected API routes, titles and share images. Secret findings unlock only after you verify you own the site.
Free scan, no signup, no code upload.FAQ
Do I really need all of this for a side project?
The secrets and database sections, yes, even for a toy. A leaked OpenAI key or an open table costs real money or exposes real people no matter how small the app is. The polish items are optional, but they're cheap and they decide how your launch looks.
My AI builder has a built-in security scan. Is that enough?
Use it, it's a good first pass. Then check what's actually deployed. Builder scans look at your project; the problems that bite are in the live site: the bundle visitors download and the tables the public key can reach.
What order should I fix things in?
Rotate any leaked keys first, then lock down data (RLS), then protect paid endpoints, then polish. A leaked key keeps working until it's revoked, no matter what you change in code.
Keep reading
Sources: Supabase: Row Level Security · Stripe: API keys