Lovable security checklist: 10 checks before you launch
Lovable gets you to a working app fast, and the security gaps it leaves are predictable. Work through these ten checks before you share the link, and again after any big change.
Short answer: Make sure no secret key is in your frontend, no .env or .git is public, every Supabase table has Row Level Security with policies tied to auth.uid(), and your site sends basic security headers over HTTPS. Checks 1, 3, 4, 8 and 9 can be automated; the rest are manual.
Lovable apps share one architecture: a Vite and React frontend that talks to Supabase from the browser, with Edge Functions for anything server-side. That means the risks are the same for almost every Lovable app, and so is the checklist. Each item is marked [scanned] if the free ShipWatch scan tests it, or [manual] if you have to check it yourself. For a shorter overview of the platform's risks, see the Lovable app security check.
Keys and files
1. No secret keys in the frontend [scanned]
Open your live app, open DevTools, press Ctrl+Shift+F and search for sk_live_, sk-proj-, sk-ant-, AKIA and sb_secret_. Any hit is a leaked secret. Rotate it at the provider, then move the call into an Edge Function. The full method is in the leaked API key checker.
2. Nothing secret in a VITE_ variable [manual]
Vite copies every VITE_ variable into the built JavaScript. That's correct for the Supabase URL and anon key and wrong for anything else. Open your project's code and look at every import.meta.env.VITE_ reference. If the name contains SECRET, PRIVATE or a paid provider, it doesn't belong there.
3. No public .env file [scanned]
Visit https://yourapp.com/.env. If you see lines like KEY=value, your environment file is public. Remove it from the deployed output, add .env to .gitignore, and rotate every key in it. A page of HTML (your app's 404 or home page) is fine.
4. No public .git folder [scanned]
Visit https://yourapp.com/.git/HEAD. If it says ref: refs/heads/main, your repository can be downloaded, history included. This mostly happens when you export from Lovable and self-host. Deploy only the build folder (dist). The .env and .git check covers per-host fixes.
Supabase
5. Row Level Security is on for every table [manual]
In the Supabase dashboard, open the SQL editor and run:
select tablename, rowsecurity from pg_tables where schemaname = 'public';
Every row should say true. Any false table can be read and changed by anyone holding your anon key, which is everyone. Fix it with alter table public.your_table enable row level security;. The Supabase Security Advisor flags the same thing.
6. Policies are tied to the user [manual]
RLS with a policy of using (true) is the same as no RLS. For user-owned data, policies should compare the row's owner to the logged-in user, for example using (auth.uid() = user_id). The Supabase RLS checklist has the exact SQL for each operation.
List every table in the public schema with its RLS status and policies. Enable RLS on any table without it. Replace any policy that uses "true" for user-owned data with policies that only allow the owner (auth.uid() = user_id) to select, insert, update and delete their rows. Show me the SQL before running it.
7. The service_role key stays server-side [manual, partly scanned]
The service_role key ignores RLS entirely. It's fine inside an Edge Function, read from a secret. It must never appear in src/. The scan flags it if it's in your live page or scripts; check your Edge Function code and repo yourself.
Headers and HTTPS
8. Basic security headers are set [scanned]
Run curl -I https://yourapp.com and look for Content-Security-Policy, Strict-Transport-Security, X-Frame-Options (or a CSP frame-ancestors rule) and X-Content-Type-Options: nosniff. Whether you can set these depends on where you host. If your host doesn't let you add headers, a host like Netlify, Vercel or Cloudflare Pages does, through a config file. Start a CSP in report-only mode so you don't break Supabase or Stripe calls.
9. HTTP redirects to HTTPS [scanned]
Open http://yourapp.com (no s). It should land on https://. Most managed hosts do this by default; custom domains are where it slips.
Logic
10. Test as a stranger [manual]
Sign up a second account in a private window. Try to open the first account's pages by changing IDs in the URL. Log out and try your app's protected pages directly. No scanner can do this for you, because it depends on what your app is supposed to allow.
What the free scan checks here (and what it doesn't)
ShipWatch's scan is passive and read-only. It covers items 1, 3, 4, 8 and 9: secret keys (Stripe, OpenAI, Anthropic, AWS, Google, Supabase service_role and sb_secret_) in your page and up to 25 linked scripts, /.env, /.git/HEAD, the four headers above, and HTTPS plus the HTTP redirect. When it sees your Supabase anon key it tells you, and reminds you to check RLS, but it does not query your tables. Items 2, 5, 6, 7 and 10 are yours. For the general version of this list, see vibe coding security: the 6 mistakes.
FAQ
- Is my Supabase anon key in a Lovable app a problem?
- Not by itself. Every Lovable app that talks to Supabase from the browser ships the anon or publishable key, and that's by design. It becomes a problem when a table has RLS off or a policy that lets everyone read or write.
- Where should secret API keys go in a Lovable app?
- In Supabase Edge Function secrets, used only inside an edge function. The frontend calls the function; the function calls the paid API. Never put a secret in client code or a VITE_ variable.
- Does a clean ShipWatch scan mean my Lovable app is secure?
- No. The scan covers leaked keys in your page and scripts, public .env and .git files, four security headers and HTTPS. It does not read your Supabase tables or test logins, so checks 2, 5, 6, 7 and 10 here stay manual.