Vibe coding security: the 6 mistakes that get AI-built apps hacked
AI tools write working code, not safe defaults. The same six mistakes show up again and again in apps built with Lovable, Bolt, v0, Replit and Cursor, and every one of them can be checked from outside in minutes.
Short answer: The six are: secret API keys in the frontend, a Supabase service_role key in the browser, a public .env file, a public .git folder, missing security headers, and no forced HTTPS. Check each one against your live site, not your editor.
Each mistake below maps to a check the free ShipWatch scan actually runs. That's deliberate: these are the problems anyone, including an attacker, can see from the outside. For a longer pre-launch list that includes manual checks like auth and payments, use the vibe coding security checklist.
1. Secret API keys shipped in the frontend
What happens: you ask the AI to add payments or an AI feature, and it calls the provider straight from the browser with a secret key. Every visitor downloads that key in your JavaScript. Bots scan for key formats constantly.
Check it: on your live site, open DevTools, press Ctrl+Shift+F, and search for sk_live_, rk_live_ (Stripe), sk-proj- (OpenAI), sk-ant- (Anthropic), AKIA (AWS) and AIza (Google).
Fix it: rotate the key at the provider first, then move the call into a server function that reads the key from a server-only environment variable. Full walkthrough: did my app leak its API key?
2. A Supabase service_role key in the browser
What happens: Supabase apps ship a public anon key on purpose, protected by Row Level Security. The service_role key skips RLS entirely. AI code sometimes swaps it in to make a permission error go away, and then anyone can read or change every table.
Check it: search your live scripts for eyJ, decode each JWT's middle part, and look for "role":"service_role". Also search for sb_secret_, the newer secret key format.
Fix it: rotate the key in Project Settings, API, use only the anon or publishable key in the frontend, and audit your RLS. The anon key is only as safe as your policies. Follow the Supabase RLS checklist; checking RLS is manual.
3. A public .env file
What happens: the .env file holding your database URL and API keys gets copied into the folder your host publishes. It's then one request away for anyone.
Check it: visit https://yourapp.com/.env. Lines like DATABASE_URL=... mean it's public. If you get your normal app page or a 404, you're fine.
Fix it: remove it from the deployed output, add .env to .gitignore, block dotfiles at the host, and rotate every value that was in it, since you can't know who downloaded it.
4. A public .git folder
What happens: you deploy the whole project folder instead of the build output, and .git goes with it. Tools can rebuild your full source and history from it, including secrets you deleted months ago.
Check it: visit https://yourapp.com/.git/HEAD. If it shows ref: refs/heads/main or similar, it's exposed.
Fix it: publish only the build folder (dist, build or .next output), block /.git at the server, and rotate any secret that was ever committed. See the .env and .git check for per-host rules.
5. Missing security headers
What happens: AI-generated apps rarely set response headers, so the browser's built-in protections stay off. ShipWatch checks four:
Content-Security-Policy: limits where scripts can load from, which blunts cross-site scripting.Strict-Transport-Security(HSTS): tells browsers to always use HTTPS for your domain.X-Frame-Optionsor CSPframe-ancestors: stops other sites from framing yours for clickjacking.X-Content-Type-Options: nosniff: stops browsers from guessing file types.
Check it: run curl -I https://yourapp.com and read the response headers.
Fix it: add them in your host's config (Netlify or Cloudflare Pages _headers, vercel.json). Roll out CSP in report-only mode first, then enforce it.
Add these response headers to every route for my host: Strict-Transport-Security: max-age=31536000; includeSubDomains, X-Content-Type-Options: nosniff, X-Frame-Options: DENY, and a Content-Security-Policy that allows only the domains this app actually uses (list them). Start the CSP as Content-Security-Policy-Report-Only.
6. No forced HTTPS
What happens: the site works over https://, but http:// still serves the page instead of redirecting. Anyone on the same network can read or alter that traffic.
Check it: run curl -I http://yourapp.com. You want a 301 or 308 with a Location: https://... header.
Fix it: turn on "always use HTTPS" or the equivalent at your host or CDN, and add HSTS from mistake 5.
What the free scan checks here (and what it doesn't)
The scan runs exactly these six checks against your live URL, passively and read-only. Keys: Stripe live secret and restricted keys, OpenAI, Anthropic, AWS access key IDs, Google API keys (medium severity, since restricted ones are often fine), and Supabase service_role and sb_secret_ keys, across your page and up to 25 linked scripts. Files: /.env and /.git/HEAD. Headers: the four above. Transport: HTTPS and the HTTP-to-HTTPS redirect. It does not log in, read your database or test RLS, or review your business logic. For Lovable-specific steps, see the Lovable security checklist.
FAQ
- What is vibe coding security?
- It's the practice of checking apps built mostly by AI prompts for the mistakes AI tools commonly make, such as secret keys in frontend code, public config files, missing Row Level Security and missing security headers, before and after launch.
- Are these six the only things that matter?
- No. They're the common mistakes that can be checked from outside. Login logic, access control between users, payment webhooks and database rules also matter and need a manual review. The broader pre-launch checklist on this site covers more of them.
- How often should I check my app?
- After every deploy that touches keys, integrations, hosting or the database, and at least weekly while you're actively shipping. AI edits can reintroduce a fixed problem without telling you.