Most of the AI-built apps I look at before launch basically work. What they're missing is the stuff you can't see from inside the app: pages Google can't read, a phone load time nobody tested, and headers the browser never mentions. We recently put numbers on how common that is, and these are the four checks I'd run on any Lovable or Replit app before telling people about it.
The numbers come from our State of AI-Built Apps 2026 report: "On 18 October 2026 we ran a passive check on every app listed in two public showcases: 533 from madewithlovable.com and 224 from the Replit community showcase. 578 of the 757 were still online." A passive check means we only read what any visitor's browser receives: the home page, its response headers, the JavaScript it sends, robots.txt and the sitemap. It can't see your database rules, sign-in or payments, so a clean result here isn't a security audit. But these four gaps are easy to find and usually cheap to close.
1. Can Google and AI assistants read your pages?
From the report: "Most of these apps are hard for search engines and AI assistants to read: 76% of the Lovable apps send crawlers an almost empty HTML page, and about half of all apps have no sitemap."
Lovable and most Vite apps render in the browser. The HTML your server sends is an empty <div id="root"> plus a script, and the words appear only after JavaScript runs. Google says it handles JavaScript apps in three phases (crawling, rendering and indexing) and that Googlebot queues pages for rendering, so there can be a delay before your content is seen. The same page adds that server-side or pre-rendering "is still a great idea because it makes your website faster for users and crawlers, and not all bots can run JavaScript" (Google Search Central: JavaScript SEO basics).
How to check: open your key pages with view-source (not the inspector, which shows the page after JavaScript). Search for a sentence from your hero section. If it isn't in the source, a crawler that doesn't run JavaScript sees a blank page.
How to fix: you don't need to rewrite the app. Prerender the pages that should be found (home, pricing, features, blog), give every route its own title and description, and use real <a href> links for navigation rather than click handlers, so crawlers can follow them. Your app screens behind a login can stay client-rendered.
2. A sitemap Google can find
About half the apps had none, and it's the easiest fix on this list. A sitemap tells Google which pages exist, which matters most for new and deep pages.
How to check: open https://your-domain.com/sitemap.xml and https://your-domain.com/robots.txt.
How to fix: write a sitemap.xml that lists every public URL and put it in Vite's public/ folder. Vite copies that folder to the root of the build as-is (Vite: the public directory). A single sitemap can hold up to 50,000 URLs or 50MB uncompressed. Then tell Google where it is in two ways: submit it in Search Console, and add a line like Sitemap: https://your-domain.com/sitemap.xml to robots.txt (Google: build and submit a sitemap). Only list pages you want found, not dashboards or settings screens.
3. How fast it loads on a phone
From the report: "In a sample of 139 apps tested for mobile speed, the main content took a median of 5.0 seconds to appear, and 87% were slower than the 2.5 seconds Google treats as good."
That measure is Largest Contentful Paint (LCP): how long until the biggest thing on screen, usually your hero image or headline, shows up. web.dev puts it plainly: "A good LCP value is 2.5 seconds or less" (web.dev: LCP).
How to check: run your home page through PageSpeed Insights on the mobile tab. Its lab data comes from Lighthouse and simulates a page load (PSI docs); if your site has enough real visitors, it also shows field data.
How to fix: in the report, the usual causes were JavaScript the page doesn't use and resources that block rendering. In a Vite app that often means one big bundle that has to download before anything appears. Split heavy routes with React.lazy, which defers a component's code "until it is rendered for the first time" (React docs), so the landing page doesn't wait for the admin dashboard. Make the hero image discoverable in the HTML, sized for phones, and give it fetchpriority="high", as web.dev recommends for the LCP image (web.dev: Optimize LCP). Prerendering from check 1 helps here too, because the headline arrives with the HTML.
4. Two security headers: Content-Security-Policy and frame protection
From the report: "Security headers are the most common gap. 93% of the live Lovable apps and 79% of the live Replit apps send no Content-Security-Policy, and 91% and 72% have no protection against being loaded inside another site's frame."
What they do:
- A Content-Security-Policy (CSP) tells the browser where your page may load scripts, styles, images and network requests from. MDN describes it as a defence against cross-site scripting, where injected code runs as if it were yours (MDN: CSP guide).
-
Frame protection stops another site from loading your app inside an invisible iframe and tricking people into clicks (clickjacking). The modern way is the CSP directive
frame-ancestors(MDN: frame-ancestors);X-Frame-Options: DENYis the older header that covers older browsers (MDN: X-Frame-Options).
How to check: run curl -I https://your-domain.com and look for content-security-policy and x-frame-options in the output. If neither is there, you're in the majority.
How to fix: a Vite build is a folder of static files, so these headers come from your host, not your React code. On Vercel they go in vercel.json (Vercel docs); on Netlify, in a _headers file (Netlify docs). The frame headers are safe to switch on straight away if you don't embed your own app anywhere. Here's a Netlify _headers file you can put in public/ so it lands at the root of the build:
/*
Content-Security-Policy: frame-ancestors 'none'
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin
The last two lines are the report's other cheap wins: nosniff stops the browser guessing file types (MDN), and a Referrer-Policy keeps full page addresses from leaking to other sites (MDN).
A full CSP that limits scripts needs more care, because a wrong policy blocks your own Supabase calls or fonts. Ship it first as Content-Security-Policy-Report-Only, which reports violations without blocking anything (MDN), use the app for a day, fix what the console reports, then enforce it. One catch: frame-ancestors doesn't work in a <meta> tag, so clickjacking protection always needs a real response header.
The short version
The report's own summary: "The fixes are mostly small: a few response headers, prerendering the pages that should be found, and a sitemap." In the apps we check, they're usually configuration, not a rebuild, and they're far cheaper to do before launch day than after.
If you'd rather not run each check by hand, our free Launch Check runs these same checks on any app in about a minute, with no sign-up. It only sees what a visitor sees, so for data access rules, sign-in and payments, go through the full pre-launch list in our Production-Ready Standard (32 checks in ten areas) or have someone review the code.
I wrote this with AI assistance and reviewed it myself.
Top comments (0)