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.
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 guide3. 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 mistakes4. 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).
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.
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.
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.
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.