Shipping fast is fine, but there is a short list worth reviewing before you open your app to the world. None of it requires being a security expert, and all of it is checkable from the outside in a couple of minutes. This guide goes through every point with three things: why it matters, how to check it yourself and how to fix it, with configuration examples for the stacks AI-built apps use most (Next.js on Vercel, Supabase, Firebase). At the end you get the two-minute version to paste into your launch list.
How attackers find these in minutes
Most breaches of AI-built apps are not clever hacks. Someone opens your site, presses F12, reads the JavaScript your app already shipped to their browser, and copies a URL your app already calls. Automated scanners do the same at scale, hitting common paths like /.env or /.git on thousands of sites a day without even knowing you exist. Everything below is something they can check without any access to your code, which is exactly why you should check it first.
1. No secrets in the frontend
Why it matters. API keys and tokens belong on the server only. When an AI assistant "connects" a service, it often drops the key straight into client code, where anyone can read it in DevTools. A leaked Stripe sk_live_ key can move real money; a Supabase service_role key hands over your whole database; an OpenAI or Anthropic key leaves you paying someone else's bill.
How to check. Open your site, press F12 → Sources tab (or Network filtered by JS), and search the files for strings like sk_live, service_role, AKIA, sk-ant, AIza or -----BEGIN. If your framework uses public-prefixed variables (NEXT_PUBLIC_, VITE_, NUXT_PUBLIC_), make sure none of them holds a secret: that prefix literally means "this goes to the browser".
How to fix. Move every secret to a server environment variable and make the call from an API route or serverless function; the browser talks to your server, never to the provider. And rotate the leaked key: once it has been in a public bundle, treat it as compromised even after you remove it. More in the secrets your app leaks in the browser.
2. HTTPS and security headers
Why it matters. Serve everything over HTTPS and redirect HTTP to it, so no one can read or tamper with traffic. Then add the headers browsers use to defend your users: Content-Security-Policy to contain injection, Strict-Transport-Security to lock in HTTPS, X-Frame-Options to block clickjacking and X-Content-Type-Options to stop the browser from "guessing" file types. Most generated apps ship with none of these by default.
How to check. Request your site with curl -I https://yourdomain.com and look at the headers that come back, or use the free security headers checker.
How to fix. In Next.js you declare them in next.config.js; on Vercel, for any framework, in vercel.json:
{
"headers": [
{
"source": "/(.*)",
"headers": [
{ "key": "Strict-Transport-Security", "value": "max-age=31536000; includeSubDomains" },
{ "key": "X-Frame-Options", "value": "SAMEORIGIN" },
{ "key": "X-Content-Type-Options", "value": "nosniff" },
{ "key": "Referrer-Policy", "value": "strict-origin-when-cross-origin" },
{ "key": "Permissions-Policy", "value": "camera=(), microphone=(), geolocation=()" }
]
}
]
}
CSP deserves its own moment: start in Content-Security-Policy-Report-Only mode, see what it would block, and switch it to enforcing once it breaks nothing. See HTTP security headers explained.
3. Database rules (RLS) on every table
Why it matters. If you use Supabase or Firebase, your app talks to the database straight from the browser, so the database rules are the only thing standing between a visitor and your data. Without them, anyone with the public key reads and writes your tables without logging in: the exact flaw behind the Lovable RLS vulnerability.
How to check. With the app open, copy a /rest/v1/ call from the Network tab and open it in an incognito window. If it returns data with no session, you are exposed. In Supabase, the Table Editor flags tables with a red "RLS disabled" badge. In Firebase, check the rules are not still in test mode (allow read, write: if true or with an expiry date).
How to fix. Enable Row-Level Security on every table with a deny-by-default policy and write per-operation rules tied to the user. In Supabase, the minimum for a table with a user_id column:
alter table public.items enable row level security;
create policy "items: own rows only" on public.items
for all to authenticated
using ((select auth.uid()) = user_id)
with check ((select auth.uid()) = user_id);
And never rely on hiding a button in the UI: the request still exists.
4. No exposed sensitive files
Why it matters. A single request to /.env can hand over every secret your app uses; /.git/config lets anyone download your whole repository; published source maps let them rebuild your original code; a forgotten backup.sql is your database in a file.
How to check. Try yourdomain.com/.env, /.git/HEAD, /.DS_Store, /backup.sql in the browser, and open one JS file from your bundle to see whether it ends with a //# sourceMappingURL= line pointing to a reachable .map. On platforms like Vercel or Netlify the .env is never deployed, but on a VPS with nginx or Apache it happens.
How to fix. Block dotfiles on the server (in nginx, location ~ /\. { deny all; }), disable source maps in production (productionBrowserSourceMaps: false in Next.js, build.sourcemap: false in Vite) and move backups out of the public folder.
5. CORS locked down
Why it matters. A CORS policy that reflects any origin with credentials lets a malicious site read your users' data using their session. Access-Control-Allow-Origin: * without credentials is harmless for public data; the danger is echoing back whatever origin asks and adding Access-Control-Allow-Credentials: true.
How to check. Request one of your API endpoints with curl -I -H "Origin: https://evil.example" https://yourdomain.com/api/anything. If the response returns that same origin in Access-Control-Allow-Origin plus Allow-Credentials: true, you have the problem.
How to fix. Keep a closed list of allowed origins (your production domains and, at most, your preview one) and compare against it; any other origin gets no header.
6. Cookies and sessions configured right
Why it matters. Session cookies should carry Secure (HTTPS only), HttpOnly (unreadable from JavaScript, so an XSS can't steal them) and SameSite=Lax or Strict (defends against CSRF). They are one line of config and close a whole class of account-takeover bugs.
How to check. F12 → Application → Cookies and look at the Secure, HttpOnly and SameSite columns of your session cookie. If you use an auth provider (Supabase Auth, Clerk, NextAuth) they usually come out right; the ones that fail are the cookies your own app adds.
How to fix. When setting the cookie: Set-Cookie: session=…; Secure; HttpOnly; SameSite=Lax; Path=/. In Express, res.cookie(name, value, { secure: true, httpOnly: true, sameSite: 'lax' }).
7. Dependencies with known vulnerabilities
Why it matters. Your app drags along hundreds of packages. A vulnerable version of a library that parses user input is an entry point you did not write but that is yours all the same.
How to check and fix. Run npm audit in the project and resolve critical and high findings first; enable Dependabot or Renovate on the repository so patches come to you. If your site exposes a public package.json, you are also advertising your versions.
8. Rate limits on endpoints that cost money or open doors
Why it matters. The login form, sign-up, email sending and any endpoint that calls a paid AI API need a limit per IP or per account. Without it, a script tries thousands of passwords or runs up a token bill overnight.
How to fix. A simple server-side rate limit (per IP and per minute) and, at paid providers, a spending cap configured in their dashboard. Auth providers usually ship with attempt limits already on: check you have not disabled them.
9. Errors that do not overshare
Why it matters. An error page with the full stack trace, the SQL query or the server path is a map for whoever is attacking. The same goes for headers like X-Powered-By that announce exact versions.
How to fix. Generic error pages in production, with the detail only in your logs. Remove X-Powered-By (in Next.js, poweredByHeader: false).
10. Domain and email ready for real users
Why it matters. If you send email (confirmations, password resets), without SPF, DKIM and DMARC it will land in spam or, worse, anyone will be able to send mail pretending to be your domain. And an expiring certificate or domain takes the whole site down.
How to check and fix. Publish SPF and DMARC records in DNS, enable DKIM at your sending provider, and turn on auto-renewal for the domain and the certificate. Half an hour of work that prevents the worst possible day.
What order to fix things in if you have one hour
- First, secrets in the frontend and database rules: the two flaws that hand real data to anyone.
- Then, exposed files and CORS: cheap to fix and heavily probed by automated scanners.
- Next, headers, cookies and error pages: they harden what already works.
- Last, dependencies, rate limits and email: less urgent on launch day, essential the week after.
Your 2-minute pre-launch checklist
- No
sk_live_,service_roleor private keys in your JavaScript; public variables reviewed. - HTTPS enforced, with HSTS, X-Frame-Options, X-Content-Type-Options and a CSP at least in report mode.
- Row-Level Security (or Firebase rules) on every table; the incognito test returns empty.
.env,.git, backups and source maps not reachable.- CORS limited to your own origins, never echoing the origin with credentials.
- Cookies with Secure, HttpOnly and SameSite.
npm auditclean of critical and high findings.- Attempt limits on login, sign-up and AI endpoints; spending caps at providers.
- Error pages without traces; no
X-Powered-By. - SPF, DKIM and DMARC published; auto-renewal on domain and certificate.
Just get it done
Instead of checking all this by hand, run Scanaris: it verifies these points and around 40 more on your live site in 30 seconds, ranks the findings by impact, and gives you a ready-to-paste prompt to fix each one with your AI, before someone else finds them. Turn on monitoring and it tells you when anything on this list breaks again after launch.
Frequently asked questions
Does this replace a professional security audit?
No. This list covers what can be checked from the outside and what fails in almost every AI-built app. An audit also reviews your business logic, permissions between users and the code itself. But without this list, the audit would start with the same items.
How long does all of it take?
Checking: ten minutes with a scanner or half an hour by hand. Fixing: between an afternoon and a day depending on how many points fail; secrets and RLS take the longest because they force you to move code to the server.
And after launch?
Repeat the list every time the AI generates a new feature that touches data or keys, and set up a weekly automatic re-scan: apps break because of changes, not because of time.