All posts
Checklist8 min read

Shipping an AI-built app? Check these 7 things before you launch

Building with Cursor, Claude Code, v0, Bolt, or Lovable is the fastest way to go from idea to deployed app that has ever existed. The catch: these tools optimize for "it works in the demo," and almost none of them add security hardening unless you explicitly ask for it.

That's not a reason to stop vibe coding. It's a reason to run a short checklist before (or right after) you ship. The seven issues below are the ones that appear most often in AI-generated apps — every one of them is quick to check and quick to fix, and most fixes are a single config change or a single prompt back to your AI tool.

1. API keys baked into your frontend JavaScript

This is the most damaging mistake and the easiest one to make. You ask your AI tool to "add Stripe payments" or "connect to OpenAI," and it wires the secret key straight into a client component. The app works — and your secret key now ships to every visitor's browser, readable by anyone who opens DevTools or greps your JS bundle.

Anything prefixed NEXT_PUBLIC_ (or VITE_, or REACT_APP_) is embedded into the client bundle at build time. Secret keys — Stripe sk_live_, OpenAI sk-, database URLs, service-role keys — must only ever be read in server-side code: API routes, server components, or edge functions.

The quick check: open your deployed site, view the page source and bundled JS, and search for sk_live, sk-, and the first characters of the keys in your .env. If any of them appear, rotate the key first, then move the call server-side.

API keys in frontend code: why it happens and how to fix it

2. .env or .git served straight from your web root

A surprising number of deployed apps will happily return their .env file — or their entire .git directory — if you just ask for it. One misconfigured static-file server or a framework template that copies everything into the public folder is all it takes.

The impact is total: database credentials, auth secrets, API keys, all in one file. An exposed .git directory is nearly as bad, because an attacker can reconstruct your full source history, including secrets that were committed and later "removed."

The quick check takes ten seconds:

curl -i https://yourapp.com/.env
curl -i https://yourapp.com/.git/config

# Both should return 404 (or 403) — never 200.
Exposed .env files: the full guide

3. No Content-Security-Policy

AI tools almost never add security headers, and CSP is the one that matters most. Without it, a single HTML-injection or XSS bug anywhere in your app lets an attacker run arbitrary JavaScript in your users' browsers — stealing sessions and exfiltrating data.

A good starting policy for most apps: default-src 'self', block objects and framing, and only allow the third-party script origins you actually use. Start strict, loosen deliberately — never the other way around.

// next.config.js
const csp = [
  "default-src 'self'",
  "script-src 'self'",
  "style-src 'self' 'unsafe-inline'",
  "img-src 'self' data:",
  "base-uri 'none'",
  "frame-ancestors 'none'",
  "object-src 'none'",
].join("; ");

module.exports = {
  async headers() {
    return [{ source: "/:path*", headers: [{ key: "Content-Security-Policy", value: csp }] }];
  },
};
CSP explained: directives, nonces, and common mistakes

4. Session cookies without Secure, HttpOnly, and SameSite

If your auth library was wired up by an AI tool with default options, it's worth verifying the session cookie flags. A cookie without HttpOnly can be read by any injected script (turning a small XSS into full account takeover), one without Secure can leak over plain HTTP, and one without SameSite makes cross-site request forgery much easier.

Open DevTools → Application → Cookies on your deployed app. Every session or auth cookie should show all three: Secure, HttpOnly, and SameSite=Lax (or Strict).

Cookie security flags: what each one protects against

5. HTTP that never becomes HTTPS (and no HSTS)

Most hosting platforms give you HTTPS automatically — but plenty of apps still respond happily on plain HTTP without redirecting, or redirect but never send a Strict-Transport-Security header. Either way, a user who types your domain into the address bar makes their first request unencrypted, and that request can be intercepted or downgraded before your redirect happens.

The fix is two lines of config: a permanent 301 redirect from HTTP to HTTPS, plus an HSTS header (max-age=31536000; includeSubDomains) so browsers refuse to ever downgrade again.

HSTS: how one header shuts down downgrade attacks

6. CORS set to allow everything

When an AI-generated frontend can't reach its own API during development, the "fix" that gets generated is almost always Access-Control-Allow-Origin: * — sometimes with credentials allowed too. That tells every website on the internet it may call your API from its visitors' browsers.

For an API that serves your own frontend, the allowed origin should be exactly your own domain, and nothing else. Wildcard CORS is only acceptable for genuinely public, unauthenticated, read-only APIs.

CORS misconfiguration: when * becomes a vulnerability

7. No SPF or DMARC on your domain

This one isn't about your app's code at all — it's about your domain. Without SPF and DMARC DNS records, anyone can send email that claims to be from [email protected], and receiving mail servers have no way to know it's forged. That's phishing against your own users, wearing your brand.

Two DNS TXT records fix it. If you send email through a provider like Resend, Postmark, or SES, their docs give you the exact SPF value; add a DMARC policy alongside it and tighten it from p=none to p=quarantine once you've confirmed legitimate mail passes.

SPF and DMARC: stopping email spoofing on your domain

Check all seven in one pass

Every item on this list is detectable from the outside, which means you don't have to work through it by hand. AppSafe runs all of these checks — plus SSL/TLS, subdomain takeover, source maps, and more — in a single scan, grades the result A–F, and gives you an AI fix prompt for each finding that you can paste straight back into Cursor or Claude Code.

The quick scan on the homepage needs no signup. The full scan (including the exposed-secrets checks) runs only on domains you've verified you own — by design, since we won't grep someone else's JS bundles for keys.

Run this checklist automatically

One free scan checks your app for all of these issues and gives you an AI fix prompt for each finding.

Scan my app free
Security Checklist for AI-Built Apps — 7 Things to Check Before Launch