Updated October 20, 2026 · ClearanceHQ
Secrets and keys
1No secret keys in your browser code
Anything your framework exposes to the browser is readable by every visitor. That includes every variable prefixed NEXT_PUBLIC_, VITE_, EXPO_PUBLIC_ or REACT_APP_. Only publishable keys belong there: Supabase anon or sb_publishable_ keys, Stripe pk_ keys and Firebase web config.
Check: build your app, then search the output folder (for example .next/static or dist) for sk_live_, rk_live_, sk-, sk-ant-, service_role and sb_secret_. Any hit is a key you must move to the server and rotate.
2No secrets anywhere in your git history
Deleting a key in a later commit doesn't remove it: anyone with the repo can still read the old commit. AI agents often paste keys into a file "just to test".
Check: run gitleaks on the whole history (gitleaks git . on current versions, gitleaks detect on older ones). Rotate every real key it finds in the provider's dashboard; rewriting history is not enough once the repo has been pushed or shared.
3No .env or .git folder on your live site
A misconfigured host can serve your .env file or .git folder as plain downloads, handing out every secret and your full source code.
curl -s -o /dev/null -w '%{http_code}\n' https://your-app.com/.env
curl -s -o /dev/null -w '%{http_code}\n' https://your-app.com/.git/HEADBoth should print 404 (or 403). Also make sure .env* is in your .gitignore.
Database
4Row Level Security on every Supabase table
Your Supabase anon or publishable key is public by design, so RLS is the only thing between a visitor and your data. Tables created in the Table Editor get RLS automatically; tables created with SQL or migrations do not.
Check: follow our Supabase anon key and RLS guide; it has the SQL query and a curl test you can run in two minutes.
5No "allow everyone" policies on private data
A policy with using (true) lets every visitor read every row. That's fine for a public blog table and a leak for profiles, orders or messages.
Fix: scope private tables to the signed-in user, for example using ((select auth.uid()) = user_id), with a matching with check on insert and update.
6The Supabase service_role / secret key stays on the server
The service_role key (or a newer sb_secret_ key) bypasses RLS completely. If it's in your frontend, your RLS doesn't matter.
Check: it should only appear in server-side environment variables, never with a public prefix. If it ever shipped to the browser or was committed, treat it as compromised and rotate or revoke it in your Supabase dashboard.
7Firebase rules are not open to the world
Test-mode rules such as allow read, write: if true; (Firestore) or ".read": true at the root (Realtime Database) make your whole database public.
Check: curl -s https://YOUR-DB.firebaseio.com/.json (or your .firebasedatabase.app URL) should answer with a permission error, not your data. Review rules so each path requires request.auth and matches the user's ID.
Auth, payments and APIs
8Every route that changes data checks the login on the server
Hiding a button in the UI is not access control. API routes, Next.js route handlers and server actions must verify the session themselves, because anyone can call them directly.
Ask your agent: "List every API route and server action that writes data or calls a paid API, and show me where each one checks the signed-in user on the server."
9Users can only touch their own records
If /api/invoices/123 returns invoice 123 to anyone who is logged in, users can read each other's data by changing the number (an IDOR bug).
Fix: always filter by the current user on the server, for example where id = ? and user_id = currentUser.id. Never trust an owner ID sent from the browser.
10Stripe webhooks verify the signature
Without signature verification, anyone can POST a fake "payment succeeded" event and unlock paid features for free.
Fix: verify every event with stripe.webhooks.constructEvent(rawBody, signatureHeader, endpointSecret) using the raw request body, and grant access from the verified webhook or a server-side lookup, never from the success-page redirect alone.
11Expensive endpoints are rate limited
AI calls, sign-up, login, password reset and email sending are the endpoints bots hammer. One unprotected AI route can run up a large bill overnight.
Fix: add per-IP and per-user limits on those routes, keep AI keys on the server, and set a budget or usage limit in your AI provider's dashboard where one is available.
Live site
12Security headers are set
Headers are cheap protection against XSS, clickjacking and downgrade attacks.
curl -sI https://your-app.com | grep -iE 'content-security-policy|strict-transport-security|x-content-type-options|x-frame-options|referrer-policy'You want a Content-Security-Policy, HSTS, X-Content-Type-Options: nosniff, clickjacking protection (frame-ancestors in the CSP or X-Frame-Options) and a Referrer-Policy.
13Session cookies are HttpOnly, Secure and SameSite
Without HttpOnly, any injected script can steal the session; without Secure, it can travel over plain HTTP.
Check: in your browser's dev tools (Application → Cookies), session cookies should show HttpOnly, Secure and SameSite=Lax or Strict.
14Source maps aren't public (unless you mean them to be)
Public source maps hand out your original, readable source code, comments and all.
Check: open a JavaScript file from your site and add .map to its URL. Next.js and Vite don't publish browser source maps by default; if you turned them on (productionBrowserSourceMaps or build.sourcemap), make sure that was deliberate.
15CORS doesn't trust every origin
An API that reflects any Origin and also allows credentials lets any website make logged-in requests on your users' behalf.
Fix: allow only your own domains, and never combine a wildcard or reflected origin with Access-Control-Allow-Credentials: true.
16Dependencies have no known exploited vulnerabilities
Old packages with public exploits are the easiest way in. Anything on CISA's Known Exploited Vulnerabilities (KEV) list is being used in real attacks.
Check: run npm audit or osv-scanner, and update anything flagged high or critical first, starting with KEV entries.
Want this checked for you?
The free check looks at your live URL: headers, cookies and exposed .env or .git files. The full scan covers your repo, git history and live site for $1 $5, with an Agent fix prompt and a free re-scan.
Get the full scan Run the free check
Automated, read-only checks. A clean result does not guarantee your app is secure.