What breaks in Lovable apps: data from 190 live apps
I ran the same outside-in scan against 190 live apps listed as built with Lovable. No login, no account, nothing a normal visitor's browser could not see. 189 of them had at least one flagged issue. Here is what came up most, what almost never came up, and how to check each item on your own app in about two minutes.
This is aggregate data only. No app names and no domains are published here, and none will be. The scan ran on 11 September 2026.
- 189 of 190 (99%) had at least one flagged issue; the average app had 4.1 (median 4) out of 17 checks
- 179 of 190 (94%) send no Content-Security-Policy header
- 172 of 190 (91%) send no X-Frame-Options header
- 150 of 190 (79%) answer 200 OK for a page that does not exist
- 127 of 190 (67%) have no DMARC record, or one set to p=none
- 3 of 190 ship a Google/Firebase API key in the front-end bundle; none shipped a Supabase service_role key or a Stripe live secret key
- 0 of 190 exposed a .env, .git or backup file, and 0 of 125 probed Supabase projects returned their schema to the public key
How the data was collected, and what it cannot see
The starting point was a public directory of projects built with Lovable: 512 listings. 428 of them were still live, and 395 of those were in English. After removing portfolios, games, agency pages and pages with no product surface (no pricing, payments, or signup), 190 apps were left, and all 190 scanned without errors. 155 run on their own domain; 35 are on a platform subdomain such as *.lovable.app. Some have since moved hosting to Vercel or Netlify, so read this as "apps built with Lovable", not "apps hosted by Lovable".
Each app got the same 17 automatic checks that the free ShipClarity scanner runs: exposed config and repo files, secret keys in the JavaScript bundle, whether a Supabase project answers schema requests with only the public key, source maps, SPF, DKIM and DMARC, HTTPS and the HTTP redirect, four security headers, the mobile viewport tag, robots.txt and noindex, the status code of a missing page, and first-response time.
What this data cannot tell you
- The scanner never logs in. Signup, password reset, one user reading another user's rows, checkout and refunds are all behind the login, and none of that is measured here.
- It does not run JavaScript. It reads the HTML the server sends and up to 8 script files referenced from the home page. A key inside a lazily loaded chunk would be missed.
- Headers are read from the home page response only. A Content-Security-Policy set through a <meta> tag instead of a header is counted as missing.
- DKIM cannot be confirmed from outside (the selector is provider-specific), so there is no DKIM number in this article.
- The sample is a directory of projects whose makers chose to list them. It is not a random sample of every Lovable app.
1189 of 190 had at least one flagged issue
Only one app out of 190 came back clean. The average was 4.1 flagged checks out of 17, the median 4, the worst 8. Most of that count comes from the four items below, which are configuration defaults nobody revisited, not code bugs. That matters for how you read the number: "99% have issues" does not mean 99% are broken for users. It means almost nobody went back over the defaults after the app started working.
Check yours in 2 minutes
Run the free scan on your URL. It is the same 17 checks used for this dataset, takes about 20 seconds, and needs no signup.
294% send no Content-Security-Policy
179 of 190 apps send no Content-Security-Policy header. CSP is the browser-side rule that says which scripts are allowed to run on your page. Without it, any script that gets injected (through a compromised third-party widget, or user content rendered without escaping) runs with full access to the page, including whatever the logged-in user can see.
This is a hosting or proxy setting, not something the AI builder writes into your app code, which is why it is missing almost everywhere.
Check yours in 2 minutes
curl -sI https://yourapp.com | grep -i content-security-policy
No output means no header. Without a terminal: open DevTools, Network tab, reload, click the first request (the document), and look under Response Headers. If your host does not let you set response headers, a proxy in front of the domain (Cloudflare, or Netlify/Vercel header rules) can add them. Start with Content-Security-Policy-Report-Only so a strict policy does not break your own scripts on day one.
391% send no X-Frame-Options
172 of 190 apps send no X-Frame-Options header. 171 of them send no CSP either, so there is no frame-ancestors rule covering the gap. That means another site can load the app inside an invisible iframe and trick a logged-in user into clicking something real: a delete button, a plan change, a share toggle.
Two more headers from the same family, for completeness: 44 of 190 (23%) lack X-Content-Type-Options: nosniff, and 23 of 190 (12%) lack HSTS.
Check yours in 2 minutes
curl -sI https://yourapp.com | grep -i -E "x-frame-options|frame-ancestors"
You want either X-Frame-Options: DENY (or SAMEORIGIN) or a CSP containing frame-ancestors 'none'. Note that frame-ancestors is ignored inside a <meta> CSP tag; it only works as a real response header.
479% return 200 for a missing page
150 of 190 apps answered 200 OK when asked for a random URL that does not exist. This is the single-page-app default: the host serves index.html for every path and lets the front-end router decide what to show. The visitor may see a "not found" screen, but search engines and uptime monitors see a healthy page.
Two practical costs. Search engines can index junk or mistyped URLs and flag them as soft 404s. And a monitor that pings a route will keep reporting "up" after that route has been deleted or renamed.
Check yours in 2 minutes
curl -s -o /dev/null -w "%{http_code}\n" https://yourapp.com/this-page-does-not-exist
If it prints 200, open the same URL in a browser too. A blank page or the home page is worse than a "not found" screen. The minimum fix is a catch-all route that shows a clear not-found page and adds <meta name="robots" content="noindex">. A real 404 status needs a host-level rule.
567% have no enforcing DMARC
127 of 190 apps have either no DMARC record at all (50) or one set to p=none (77), which only monitors and blocks nothing. All 127 are on their own domain: counted against the 155 custom domains in the set, that is 82%. The 35 apps on a platform subdomain inherit the platform's record. On top of that, 52 custom domains (27% of all apps) have no SPF record either.
Without an enforcing DMARC policy, anyone can send email that claims to be from your domain, and your own signup and password-reset emails are more likely to land in spam. If that second part sounds familiar, there is a separate guide: Lovable password reset email not arriving.
Check yours in 2 minutes
dig TXT _dmarc.yourdomain.com +short # macOS / Linux nslookup -type=TXT _dmarc.yourdomain.com # Windows
No answer means no record. p=none means monitor-only. The target is p=quarantine or p=reject, but only after SPF and DKIM are set up for the provider that actually sends your mail. Switching to reject before that will block your own emails.
63 apps ship an API key in the bundle
3 of 190 apps had a key matching a known secret pattern in the JavaScript sent to every visitor. All three were Google/Firebase API keys (the AIza... format). That is less dramatic than it sounds: this type of key is designed to sit in client code, and it is fine as long as it is restricted by HTTP referrer and by API in the Google Cloud console. An unrestricted one can be copied and used on someone else's site at your expense.
The more useful number is the zero: none of the 190 shipped a Supabase service_role key, a Stripe live secret key (sk_live_), or an AWS access key in the scanned bundles. Those are the ones that would hand over the database or the payment account. The Supabase anon key, which most of these apps do ship, is public by design and was not counted as a finding.
Check yours in 2 minutes
Open your live app, open DevTools, press Ctrl+Shift+F (Cmd+Option+F on a Mac) to search across all loaded files, and search for service_role, sk_live_, AKIA and AIza. A hit on the first three means rotate the key now and move the call to a server-side function. A hit on AIza means open Google Cloud Console, Credentials, and confirm the key has both a referrer restriction and an API restriction.
7What held up
The scary items were rare. Out of 190 apps:
- 0 exposed a .env, .git/config, config or backup file at a guessable path.
- 190 load over HTTPS. 3 also still answer on plain HTTP without redirecting.
- 140 (74%) use Supabase. For 125 of them the scanner could test the public key against the database's REST root, and none returned the schema. The other 15 could not be probed.
- 3 publish source maps in production, 3 have the home page set to noindex, and 19 (10%) serve no robots.txt.
Check yours in 2 minutes
curl -s -o /dev/null -w "%{http_code}\n" https://yourapp.com/.env
curl -s -o /dev/null -w "%{http_code}\n" http://yourapp.com/
The first should not be 200 with real file contents (a 200 that returns your normal app page is the soft-404 behaviour from point 4, not a leak). The second should be a 301 or 308 redirect to HTTPS.
What to take from this
Everything above is visible from outside, and almost all of it is configuration: headers, DNS records, a catch-all route. An afternoon covers it. None of it tells you whether the app works for a paying user. The failures that cost customers live behind the login, where no scanner goes: a signup that silently fails, a reset link that points at the old preview domain, one account seeing another account's data, a checkout that charges twice. This dataset says nothing about how common those are, because it could not look.
See where your own app stands
The free scanner runs the same 17 checks used for this dataset on your URL. About 20 seconds, no login, no signup.
Run the free scan Send me your URLPrefer a person to look? Leave your URL in the form on the home page and I will reply within 24 hours with 3 things I found by hand. Free, no call.