Field report from Roast & Rebuild
The pre-launch checklist every vibe-coded app skips.
We audited more than 150 apps built on Lovable, Bolt, v0, Cursor, Replit, Tempolabs, Claude Code, and Codex. The same six gaps kept showing up. Here's the pattern and how to check yours before real users find it first.
Delivered in 2-3 minutes · No login · One-time payment · 7-day money-back
What we kept finding across 150 audits
AI tools are excellent at the build. They're indifferent to the deploy. Across 150 audits of vibe-coded apps, we kept finding the same gaps — and they weren't bad code. They were missing code. The boring middleware between "works on my laptop" and "safe in production": rate limits, security headers, server-side keys, a privacy policy that doesn't still say [YOUR COMPANY NAME]. Each one is a 30-minute fix. Skipped together, they're how AI-built apps stop being apps and start being liabilities. This is what to check before you point real traffic at the URL.
What we find isn't bad code. It's missing code — the boring middleware between “works on my laptop” and “safe in production.”

Jacob Anderson
Want human support on your project? I'm here.
The six gaps in order of how often we see them
Percentages reflect the 150 vibe-coded apps we've audited. The full $19 audit flags each of these on YOUR specific project with file-and-line citations.
No rate limiting on AI-backed endpoints
The AI tool wrote the happy-path /api/chat endpoint and stopped there. There's no middleware between the public internet and your $3-per-call LLM. We routinely find production apps where a single attacker with a for-loop drains a month of API budget overnight.
Fix: Add per-IP rate limiting on every endpoint that calls a paid third-party API. Upstash Redis + framework middleware is 30 minutes of work and the cheapest insurance you'll buy.
API keys exposed in the frontend bundle
Signature: a NEXT_PUBLIC_OPENAI_API_KEY or NEXT_PUBLIC_ANTHROPIC_API_KEY referenced from a client component. Anyone with browser devtools grabs it from the network tab inside 90 seconds and uses it until you notice the bill.
Fix: Move every third-party API call to a server-side Route Handler. Read keys from process.env on the server only. Rotate any key that ever lived client-side — assume it's compromised.
Missing critical security headers
No Content-Security-Policy. No Strict-Transport-Security. No X-Content-Type-Options. These are framework-level concerns no AI tool emits by default — they don't show up in a "build me a landing page" prompt. They're also a single config block that closes three quarters of the drive-by exploit surface.
Fix: Add the headers in next.config.mjs (or your framework's equivalent). Strict defaults: CSP default-src 'self', HSTS max-age=63072000, X-Content-Type-Options nosniff, Referrer-Policy strict-origin-when-cross-origin.
API responses returning too much data
/api/users/me returning the password hash. /api/posts/123 returning the author's email. The endpoint was written to serve a UI need, and "serve a UI need" became "return every column in the row." The hardest item on this list to spot from outside — which is why it's the one most often shipped.
Fix: Decide what each endpoint is allowed to return and write it down. Use explicit Zod schemas or DTOs to whitelist response fields. Never spread the raw DB row into the response body.
Source maps shipped to production
A .map file next to every .js bundle, freely fetchable, containing your original source paths and function names. Not a vulnerability on its own — a force multiplier for the next one. Whatever someone was going to find by reverse-engineering, they now find in five minutes by reading your actual code.
Fix: Disable source map generation in production builds (productionBrowserSourceMaps: false in Next.js). If you need them for Sentry, upload via the build pipeline instead of serving from /public.
No privacy policy — or one that still has placeholder text
Either no /privacy page at all, or a template with [YOUR COMPANY NAME] still in it. The legal exposure for indie SaaS is rarely catastrophic — but payment processors check, browser extensions check, and increasingly app stores check. Missing privacy policies have started getting Stripe accounts paused.
Fix: Ship a real /privacy page that names your company, lists the data you collect, says how long you keep it, and gives contact info. A template is fine if you actually fill it in.
Example fix prompts you can paste right now
Three of the prompts our audit hands back — written for Cursor or Claude. Use them as-is or wait for the full audit to ground them in your actual files.
Install @upstash/ratelimit and @upstash/redis. Create middleware.ts at the project root (or extend the existing one). Define three rate-limit buckets keyed by route prefix: ai (3 req / 60s sliding window) — any route under /api/chat, /api/generate, /api/completion, or that imports the OpenAI/Anthropic SDK payment (10 / 60s) — /api/stripe, /api/checkout, /api/payment default (60 / 60s) — everything else under /api/* Detect client IP via x-forwarded-for (first entry), falling back to request.ip. On limit-exceeded, return HTTP 429 with a Retry-After header in seconds and JSON { error: "Rate limit exceeded" }. FAIL OPEN on any Upstash error — log to console but pass the request through. Skip the limiter entirely if KV_REST_API_URL is unset (treat as dev-mode opt-out). Verify by curling one AI route four times in succession; the fourth call should return 429 with Retry-After. Add a one-line README note: "Rate limiting requires KV_REST_API_URL and KV_REST_API_TOKEN — install the Upstash integration in Vercel."
Step 1 — Enumerate every NEXT_PUBLIC_* env var defined in .env, .env.local, .env.example, plus every reference in the codebase (grep -r "NEXT_PUBLIC_" src/ app/ components/ lib/). Step 2 — Classify each: KNOWN-PUBLIC (leave alone): pk_live_/pk_test_ Stripe publishable keys, NEXT_PUBLIC_POSTHOG_KEY (phc_), NEXT_PUBLIC_SUPABASE_ANON_KEY (RLS-protected), Sentry DSN, GA measurement ID LEAKED-SECRET (must move server-side): anything matching sk_, sk-, AIza, ANTHROPIC_API_KEY, OPENAI_API_KEY, SUPABASE_SERVICE_ROLE_KEY, RESEND_API_KEY, or any token/password not on the known-public list Step 3 — For each LEAKED-SECRET: a) Move the consuming code into a new server route at app/api/<feature>/route.ts. Client fetches from that route instead. b) Rename the env var (drop NEXT_PUBLIC_ prefix) in .env files AND code references. c) Rotate the secret in the provider dashboard. Assume compromised — anyone with devtools could have grabbed it. d) Check git history: git log -p -S "<leaked-value>" -- '*'. If the value appears, the key is permanently public; rotation is mandatory not optional. Step 4 — Verify: run npm run build, then grep -r "sk_\|AIza\|ANTHROPIC_API_KEY\|OPENAI_API_KEY" .next/static/. Expected: zero hits. Any hit means a secret is still in the client bundle.
Add a headers() async function in next.config.mjs returning these security headers, applied to source "/(.*)": Strict-Transport-Security: max-age=63072000; includeSubDomains; preload X-Content-Type-Options: nosniff X-Frame-Options: DENY Referrer-Policy: strict-origin-when-cross-origin Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=(self "https://js.stripe.com") Content-Security-Policy: (see below — joined into one string with "; " between directives) CSP starter — adjust the allowlists for YOUR third parties (Stripe, PostHog, Sentry, Cloudinary, etc.): default-src 'self' script-src 'self' 'unsafe-inline' 'unsafe-eval' https://js.stripe.com https://*.posthog.com style-src 'self' 'unsafe-inline' img-src 'self' data: https: blob: connect-src 'self' https://api.stripe.com https://*.posthog.com frame-src 'self' https://js.stripe.com https://hooks.stripe.com frame-ancestors 'none' base-uri 'self' form-action 'self' Note: 'unsafe-inline' and 'unsafe-eval' on script-src are currently required for Next.js App Router hydration. Removing them needs a nonce-based middleware setup — separate change. Verify in dev: npm run dev, load the site, watch the browser console for CSP violations. Add each blocked origin to the appropriate directive until the console is clean. Then npm run build && npm start and re-verify against the production bundle. After deploy: scan the live URL at https://securityheaders.com. Target an A grade — the report lists any missing header with the exact value to add.
The 8-module audit covers all six — and 30+ more.
Plus a 90-day founder roadmap, brand positioning, copy-paste fix prompts, and a shareable Vibe Score (0–100). Run the six checks above yourself in an afternoon. Or audit your whole app — security, scale, conversion — in 2 minutes for $19.
Questions we get on this checklist
See which of the six your app has — in 2 minutes.
The full audit runs the checklist on YOUR specific code, then extends it with security, scale, conversion, and growth. One-time $19, no subscription.
Audit My Tech — $19Built with a specific tool? See the tool-specific audit pages for Lovable, Bolt.new, v0.dev, Cursor, Replit, Tempolabs.