Lovable app works in preview but not after deploy
Guide · October 15, 2026
Preview is fine. You publish, or you point Cloudflare Pages / Netlify at the GitHub repo, and the live site is blank, talks to the wrong API, or the build fails. The React did not change. The environment did.
Lovable preview has your committed .env and injects Cloud secrets for you. The external host does not, unless you copy them into its Production env and rebuild.
Sources: Vite env variables, Lovable Secrets vs VITE_, and hosting outside Lovable (browser values are embedded at build time).
What it looks like
- Preview loads data. Production is empty, spins, or shows
Failed to fetch. - The deploy is green, but DevTools → Network on the live origin hits
undefined.supabase.co,localhost, or the preview project. - Build log: missing env, a schema that refuses
undefined, ornpm run builddies on a secret a plugin expected. - Lovable Cloud functions work in preview. The same call 500s on the live host.
- Prod login or data looks like a different (or empty) Supabase project than preview.
Open a private window on the live origin. Preview cookies and preview env will lie to you.
Four causes we keep seeing
1. VITE_ vars on Preview, missing in Production
Cloudflare Pages and Netlify keep two buckets. Preview (branch deploys, deploy previews) is not Production. People paste VITE_SUPABASE_URL and the publishable key into Preview, confirm the preview URL, and never open the Production tab.
Or they set Production after the last production build. Saving the var does not patch the files already live. You have to redeploy so the build sees them.
Lovable preview reads the committed .env. If you gitignored it later "for security," Lovable can still preview from its copy while the Git-connected host builds without those keys. Put VITE_* in the host Production env, or keep the committed .env Lovable expects. Do not put backend secrets in that file.
2. Secrets that never left Lovable
Lovable Secrets (STRIPE_SECRET_KEY, RESEND_API_KEY, OpenAI, webhook signing secrets) inject into Cloud Edge Functions. They are write-only in Lovable and they are not in git.
Cloudflare Pages, Netlify, or a connected Supabase project will not invent them. Copy each name the server actually reads into the host secrets UI (or Supabase Edge Function secrets). A function that 500s only in production, with a missing-env line in the log, is this.
Lovable will reject a VITE_ name in Secrets. Those are browser values. The opposite mistake is putting SUPABASE_SERVICE_ROLE_KEY in a VITE_ var so it ships in the bundle. That is a leak, not a deploy fix.
3. Vite inlines VITE_* at build, not at runtime
Vite statically replaces import.meta.env.VITE_* with a string during vite build. The live JS already contains the value (or undefined). Changing a Pages / Netlify env var and refreshing the site does nothing.
const supabaseUrl = import.meta.env.VITE_SUPABASE_URL;
// after build this is a string literal, e.g. "https://abcd.supabase.co"
// or undefined, which you then concatenate into a broken request URL
Lovable's external-hosting docs are blunt: set VITE_SUPABASE_URL and VITE_SUPABASE_PUBLISHABLE_KEY before the build. After you change env, retry the latest production deployment (or push a commit) so npm run build runs again. A cache-only "promote preview" without a rebuild keeps the old literals.
process.env.VITE_* in client code is the wrong API. Vite exposes import.meta.env. If the build "works" and the app still has empty config, search the bundle for the project ref you expect. If it is missing, the build never saw the var.
4. Supabase URL / anon key mismatch
Preview might still point at Lovable Cloud or a dev project. Production might have the URL from project A and the publishable (anon) key from project B, or an old key after a rotate. The client "connects." Rows, RLS, and Auth are the other project.
Names drift too: older templates used VITE_SUPABASE_ANON_KEY; current Lovable .env uses VITE_SUPABASE_PUBLISHABLE_KEY. If production only has the old name, createClient gets undefined for the key.
On a Cloud backend you also need the non-VITE_ pair on the server: SUPABASE_URL and SUPABASE_PUBLISHABLE_KEY. Browser and server pointing at different projects is a quiet production-only bug.
Quick checklist
- Private window, live hostname. DevTools → Network: which host do
/rest/v1or function calls hit? Compare that to preview. - Host env screens, both tabs: Production and Preview.
VITE_SUPABASE_URLand the publishable/anon key must exist on Production, same project. - After any env change, retry a clean production build. Do not only refresh the live site.
- Search the built JS for your Supabase project ref. Missing or
undefinedmeans the build did not see the var. - Secrets the functions read exist on the live host (or in Supabase secrets), not only in Lovable Cloud.
- Build failed? Read the log for the missing name. Set that name on Production, rebuild.
- Login loops or bounce to localhost is a different allowlist: preview vs production login.
Minimum Production set for a typical Lovable + Supabase frontend
VITE_SUPABASE_URL=https://YOUR_PROJECT.supabase.co
VITE_SUPABASE_PUBLISHABLE_KEY=eyJ... # anon / publishable, not service_role
VITE_SUPABASE_PROJECT_ID=YOUR_PROJECT # if the template reads it
Plus every secret Edge Functions actually call. Rebuild after you save.
Questions
Why does my Lovable app work in preview but break after deploy?
Lovable preview injects Cloud secrets and reads your committed .env. Cloudflare Pages, Netlify, and similar hosts do not. Production often has VITE_ values only on the Preview env, missing secrets, or a Supabase URL and key from a different project.
I set the environment variables. Why is production still broken?
Vite replaces import.meta.env.VITE_* with a string at build time. Saving a var in the host UI does not change the files already deployed. Retry the latest production deployment so the build runs again with the new values.
Should VITE_ values go in Lovable Secrets?
No. Secrets are for backend values used by Edge Functions. VITE_ names are build-time browser values and belong in .env. Lovable rejects a VITE_ prefix in Secrets. They still have to exist on the external host for a production build.
Preview and production hit different Supabase projects. Is that expected?
Only if you meant to. A mismatched URL or publishable key is a common deploy bug: the live app talks to the preview project, an empty project, or a URL and key that do not belong together. Compare the Network host on both origins.
The build itself fails. Is that the same problem?
Often. A missing VITE_ value, a secret a build plugin reads, or a schema that refuses undefined will fail npm run build on the host even though Lovable preview still runs. The build log names the variable. Set it on Production, then rebuild.
Login only broken on the live domain? That is Site URL / Redirect URLs, not env: preview vs production login. Save spinner succeeds and the row does not change: RLS silent updates.
Want a look at the live host? Paste the production URL into the free scan. It checks for leaked keys and missing headers on the public page, not the preview.
Run a free security scan