Pre-launch security checklist for vibe-coded apps (one hour, no security expertise required)
You're about to ship the app you built with Lovable, Bolt, v0, Cursor, or Replit. Before you press deploy, run this 60-minute checklist — 12 items that catch the bugs that actually leak data on launch day. Written for solo founders who don't want to learn security.
You're about to ship. The app works. You've tested the happy path. Your friends say it's cool.
Before you press deploy, run this checklist. 60 minutes, 12 items. Each item maps to a real incident that has happened to a real founder shipping a real AI-built app this year.
You can pretend the entire industry already learned these lessons; you can also just check the 12 items.
Database (15 min)
### 1. RLS on every Supabase table
Open Supabase → Authentication → Policies. Every table should show "RLS Enabled." If any table shows "RLS Disabled," that table is publicly readable by anyone with your anon key.
-- For each table missing RLS:
alter table public.your_table enable row level security;Then add at least a default-deny policy or a per-user policy. Full guide.
### 2. Service-role key is server-side only
Search your repo for service_role and SUPABASE_SERVICE_ROLE_KEY:
grep -rn 'service_role\|SUPABASE_SERVICE_ROLE_KEY' .For each match, ask: does this file run in the browser? In Next.js: anything in app/ or pages/ without "use server" at the top, anything in components/, anything in lib/ imported by either — runs in the browser. The service-role key cannot live there.
If you find one in client code: rotate immediately + move to server-side only.
### 3. JWT secret not exposed
In Supabase dashboard → Settings → API, your JWT secret must NOT be readable by the anon SQL role. Test:
-- As anon role:
select * from auth.config;If this returns the JWT secret, you have a critical misconfig. Newer Supabase projects fix this by default; older projects may still be exposed. Revoke anon access.
Authorization (10 min)
### 4. Every API route with params.id checks ownership
For every route under app/api/[*]/[id]/route.ts that returns user-specific data, verify the handler compares the resource's owner against the authenticated user. Missing ownership check = BOLA / IDOR.
grep -rln 'params.id' app/api/For each result, open the file and verify:
const { data: user } = await supabase.auth.getUser();
if (resource.user_id !== user.id) return new Response(null, { status: 404 });### 5. Middleware actually runs on the routes you think
Test:
curl -i https://your-app.com/api/admin/users
curl -i -H "x-middleware-subrequest: src/middleware" https://your-app.com/api/admin/usersThe first must return 401/403/redirect, not the data. The second must also be blocked — if it's not, you have CVE-2025-29927 and you're on a vulnerable Next.js. Upgrade. Full write-up.
### 6. Admin routes require admin role, not just authenticated
Sign in as a regular user. Hit an admin route (/api/admin/*, /admin). If the response is the admin data instead of 404, your middleware checks for "logged in" but not for "is admin." Pattern fix.
Secrets (10 min)
### 7. No keys in git history
git log -p --all -S 'sk-' | head -50
git log -p --all -S 'AKIA' | head -50
git log -p --all -S 'ghp_' | head -50
git log -p --all -S 'eyJ' | head -200 # JWTs including Supabase service-roleEvery match is a leak. Rotate the matched key immediately at the provider — don't bother removing the commit (history rewrites don't help once the key is in scrapers' pools). Full postmortem.
### 8. Spending limit on every API provider
For every paid API in your stack (OpenAI, Anthropic, Stripe, AWS), log into the provider and set a hard spending limit. Default limits are usually "no limit." A leaked key without a cap can become a $4,000 bill overnight.
- OpenAI: dashboard → billing → limits → set hard limit to 5x your normal monthly spend
- Anthropic: console → plans → spending limit
- Stripe: dashboard → billing → set radar rules
- AWS: Billing → budgets → max-spend cap (uses AWS Budgets + Service Quotas)
### 9. .env files are in .gitignore
cat .gitignore | grep -E '^\.env|env$'Should match .env, .env.local, .env.production. If any are missing, add them now. Then check git history:
git log --all -- .env .env.local .env.productionIf any of those have ever been committed, every secret in them is exposed. Rotate everything.
Webhooks (5 min)
### 10. Every webhook endpoint verifies signatures
For every webhook route (/api/webhooks/stripe, /api/webhooks/clerk, /api/webhooks/github), open the handler. The first thing it does should be verifying the request's signature against a shared secret:
// Stripe example:
const sig = req.headers.get('stripe-signature');
const event = stripe.webhooks.constructEvent(
await req.text(),
sig,
process.env.STRIPE_WEBHOOK_SECRET
);
// If constructEvent throws, the signature is invalid — reject.If your webhook handler reads the body and does work without verifying the signature, an attacker can forge events (fake payments, fake user creation, fake auth events). Full guide.
Headers + transport (5 min)
### 11. Security headers are set
curl -I https://your-app.com/You should see:
Strict-Transport-Security: max-age=31536000— forces HTTPS for the next yearX-Frame-Options: DENY(or CSP equivalent) — defends against clickjackingX-Content-Type-Options: nosniff— defends against MIME-sniff bugsContent-Security-Policy— defends against XSS at the browser level
Vercel handles HSTS by default; the rest you configure in next.config.js headers function. Full pattern.
### 12. No HTTP anywhere
Every URL in your app should be HTTPS. Test by trying http://your-app.com (no s) — should redirect to https://. Any third-party API call your app makes should also be HTTPS — search for http:// in your code:
grep -rn 'http://' app/ lib/ pages/Most matches will be comments / docstrings; legitimate http:// calls in 2026 are rare. Each match is worth a 30-second look.
Past the checklist
If you ran all 12 items and fixed what you found: ship. Watch your error rate carefully for 24 hours; a real attack often shows up as a spike in 401s + 404s as someone probes.
If the checklist took more than 60 minutes: that's the signal that you have material security debt. Two paths:
1. Sleep on it before launching. A 90-minute checklist run that found 4 issues means more issues are likely lurking. Defer the launch by 24 hours; do a deeper audit. The opportunity cost of one more day is real but small; the cost of launching with a bug that gets tweeted is unbounded.
2. Install [Securie](/signup) before you launch. The Day-1 specialists run the checklist's items 1-7 automatically on every PR — sandbox-verified, no false positives. Solo Founder tier ($49/mo) adds the Production-Readiness Certificate, a public verifiable URL listing the 50-control checklist evaluated against your codebase. Paste it into your launch announcement; that signals to procurement-conscious customers that you took the work seriously.
Stop the lock-step manual audit
The checklist above is a one-time launch gate. The right long-term answer is automation — every PR runs the same checks, every deploy is gated on the same surface, every commit produces a signed attestation.
Securie is built for that loop. Two minutes to install; zero engineering to maintain. The first scan finds the bugs the launch checklist missed; the recurring scans prevent regressions on every change.
Related
Related posts
A prospect just emailed asking 'is your app secure?' You don't have a real answer. Here is the honest playbook — what to say, what evidence to point at, and how to turn this question from a deal-stopper into a deal-accelerator. Written for solo founders who don't want to lie.
You built it. It works. You're about to launch. Here are the 14 things solo founders most often get wrong on launch day — from forgetting to set spending limits to shipping with a default Cursor secret in source. The honest playbook for shipping an AI-built app in 2026.
It's 3 AM. You scrolled X and saw a tweet about a Lovable / Bolt / v0 app leaking customer data. You start wondering if yours is next. Here is the exact checklist to run in the next 30 minutes — what to check, what to fix first, and how to stop having this problem.
Every AI-generated Next.js app ships with middleware.ts that looks like it gates admin routes. Half of them do not actually run on the routes they think they run on. Here is the 5-minute test, the canonical bugs, and the fixes — written for solo founders who do not want to read the matcher RFC.