Deploy

Works in Preview, Blank Page in Production? Fix Missing Environment Variables

9 min read · Updated October 18, 2026 · Checked against official docs

Your app works perfectly in the Lovable or Bolt preview, or on localhost in Cursor. You deploy it to Vercel, Netlify or Cloudflare, open the link, and get… nothing. A white page. No error on screen, no “404”, just blank.

The most common cause is missing environment variables. Your preview had values like VITE_SUPABASE_URL, and your new host doesn’t. This guide shows how to confirm that in two minutes, then how to fix it on each host.

Is it blank only when you refresh a sub-page like /dashboard? That’s a different problem: routing. See Fix 404 on Page Refresh in a Vite + React App. This guide is for a blank page even on the home page.

Why the preview works and production doesn’t

Most Lovable apps, most Bolt projects and many Cursor projects are Vite + React apps. Vite reads environment variables when it builds your app, not when a visitor loads it. Its docs say VITE_* values are “bundled into your source code at build time”. (Vite: Env variables and modes)

So the values have to exist on the computer that runs the build. That’s where previews and production differ:

  • Cursor (and any local project): your values live in .env or .env.local on your laptop. Those files are usually gitignored, so they never reach GitHub, and your host’s build never sees them.
  • Bolt: the preview reads Bolt’s Secrets panel. Bolt’s export doesn’t include those values, so they’re missing after you deploy somewhere else. (Vercel: Deploy a Bolt.new app with Vercel Drop)
  • Lovable: the frontend values (VITE_SUPABASE_URL, VITE_SUPABASE_PUBLISHABLE_KEY, VITE_SUPABASE_PROJECT_ID) live in a committed .env file. Lovable’s docs tell you to set those same variables on any outside host. If that .env was deleted or gitignored, or the host has a wrong value under the same name, production builds without them. (Lovable: Deploying and hosting outside Lovable)

When a variable is missing, Vite replaces import.meta.env.VITE_SUPABASE_URL with undefined. Your Supabase client is usually created the moment the app starts, so it throws an error before React draws anything. Result: a blank page.

Step 1: Confirm it in the browser console (2 minutes)

  1. Open your live site in Chrome.
  2. Open DevTools: Ctrl + Shift + J on Windows or Linux, Cmd + Option + J on a Mac. That jumps straight to the Console tab. (Chrome DevTools: Open DevTools)
  3. Refresh the page and look for red errors.

These messages mean a Supabase variable was missing or wrong at build time. They come straight from the supabase-js library’s code:

Uncaught Error: supabaseUrl is required.
Uncaught Error: supabaseKey is required.
Uncaught Error: Invalid supabaseUrl: Must be a valid HTTP or HTTPS URL.

The third one usually means the value has a typo, extra quotes, or is missing https://. You may also see a generic TypeError: Cannot read properties of undefined, pointing at code that reads a config value.

If the console shows something else, like JavaScript files failing to load from /dashboard/assets/..., read the “After the fix” section of the 404 on refresh guide. Copy the exact error text before you ask your AI tool for help. It’s the most useful thing you can give it.

Step 2: List the variables your app actually reads

Variable names must match exactly, including upper and lower case. Search your code for every import.meta.env. use:

grep -rn "import.meta.env.VITE_" src

Or open your project’s .env.example or .env if it has one. For a typical Lovable or Supabase app you’ll see something like:

VITE_SUPABASE_URL=https://your-project-id.supabase.co
VITE_SUPABASE_PUBLISHABLE_KEY=sb_publishable_...

Older tutorials and older projects name the key VITE_SUPABASE_ANON_KEY and use a legacy anon key starting with eyJ. That still works for now (Supabase is retiring the legacy keys by the end of 2026), but use whatever name your code reads. If the code says VITE_SUPABASE_ANON_KEY and you add VITE_SUPABASE_PUBLISHABLE_KEY, it’s still missing.

Where to find the Supabase values: open your Supabase project’s Connect dialog, which shows the URL and publishable key ready to paste. Every key is listed under Settings → API Keys. (Supabase: API keys) For Lovable’s built-in backend (Cloud), copy them from the .env file in Lovable’s code editor or your synced repo.

The two rules before you paste anything

Rule 1: browser code only sees VITE_ variables. By default, Vite only exposes variables that start with VITE_ to your app’s code. A variable named SUPABASE_URL is undefined in the browser, even if you set it perfectly on your host. (Vite: Env variables and modes)

Rule 2: never put a secret key in a VITE_ variable. Anything with that prefix ends up in the JavaScript every visitor downloads. Vite’s docs say outright that VITE_* variables “should not contain sensitive information such as API keys”.

Safe as VITE_ (public by design)Never as VITE_ (secret)
Supabase project URLSupabase secret key sb_secret_… or legacy service_role key
Supabase publishable key sb_publishable_… (or legacy anon key)Stripe secret key sk_… or webhook secret whsec_…
Stripe publishable key pk_…OpenAI, Anthropic or other AI API keys

Supabase’s publishable key is safe to expose because Row Level Security decides what it can reach. The secret key bypasses Row Level Security entirely. (Supabase: API keys) If your app only “works” with a secret key in the browser, the real fix is moving that call into server code (a Supabase Edge Function or a serverless function), not exposing the key. Our environment variables and API keys guide explains this in more detail, and Supabase RLS for beginners covers the security rules that make the publishable key safe.

Already pushed a secret into a VITE_ variable on a live site? Treat it as leaked and follow Leaked an API Key on GitHub? What to Do in the Next 30 Minutes.

Step 3: Add the variables on your host, then redeploy

The redeploy part matters on every host. Because the values are baked in at build time, saving a variable changes nothing until a new build runs.

Vercel

  1. Open your project and select Environment Variables in the sidebar.
  2. Enter the Name (for example VITE_SUPABASE_URL) and Value.
  3. Choose the environments it applies to. Make sure Production is ticked. Tick Preview too if you use preview deployments.
  4. Click Save.
  5. Redeploy: open Deployments, click the ⋯ menu on the latest deployment and choose Redeploy. Pushing a new commit also works.

Vercel’s docs are clear that variable changes “only apply to new deployments”. (Vercel: Managing environment variables · Vercel: Managing deployments) More on the rest of the setup in How to Deploy a Cursor or Bolt Project to Vercel.

Netlify

  1. Open your site and go to Project configuration → Environment variables.
  2. Add each variable. If you limit its scopes, keep Builds selected, because that’s when Vite reads it.
  3. Redeploy: go to the Deploys page and choose Trigger deploy.

Netlify notes that “environment variable changes require a build and deploy to take effect”. (Netlify: Environment variables · Netlify: Scopes · Netlify: Manage deploys)

Drag-and-drop gotcha: if you deploy by dragging a dist folder onto Netlify, Netlify doesn’t run a build, so variables set in its dashboard never reach your app. (Netlify: Create deploys) Build on your own machine with the variables in place, or connect your Git repo instead.

Cloudflare Pages

  1. In the Cloudflare dashboard, go to Workers & Pages and select your Pages project.
  2. Go to Settings → Variables and Secrets → Add, enter the name and value, and Save. You can set different values for Production and Preview. (Cloudflare Pages: Environment variables)
  3. Start a new deployment, for example by pushing a commit. The current deployment was built without the value, so it stays blank until it’s rebuilt.

Cloudflare Workers (Workers Builds)

If your app is deployed as a Worker with static assets and builds from Git, there are two places for variables, and the obvious one is the wrong one for Vite:

  • Settings → Build → Build variables and secrets: available during the build. Put your VITE_ values here.
  • Settings → Variables & Secrets: runtime values for Worker code. Vite never sees these when it builds your frontend.

Cloudflare’s docs spell it out: build variables “will not be accessible at runtime”, and runtime variables live in a separate section. (Cloudflare Workers Builds: Configuration) Then push a commit or retry the build. If you deploy with wrangler deploy from your own computer instead, the build runs on your laptop and uses your local .env.

Lovable apps on an outside host: one extra check

Projects Lovable created from May 13, 2026 use TanStack Start, which also runs server code. Lovable’s docs say these need the browser values (VITE_SUPABASE_URL, VITE_SUPABASE_PUBLISHABLE_KEY) and server copies without the prefix (SUPABASE_URL, SUPABASE_PUBLISHABLE_KEY), plus any secrets your server functions read. They also need a host that runs the server part, not just static files. (Lovable: Deploying and hosting outside Lovable) Check package.json for @tanstack/react-start to see which kind you have.

Also remember that a value set on the host wins over the same name in a .env file. Vite’s docs say variables that already exist when it runs “have the highest priority”. So a wrong value saved under the same name in your host’s dashboard overrides the correct one in Lovable’s committed .env. (Vite: Env variables and modes)

Step 4: Make the next failure obvious

A blank page is the worst error message there is. Add a check so a missing variable explains itself, right where your Supabase client is created (in Cursor and Bolt projects that’s often a file like src/lib/supabase.ts). In a Lovable project, ask Lovable to add the check for you rather than hand-editing its generated files:

const url = import.meta.env.VITE_SUPABASE_URL;
const key = import.meta.env.VITE_SUPABASE_PUBLISHABLE_KEY;

if (!url || !key) {
  throw new Error(
    "Missing VITE_SUPABASE_URL or VITE_SUPABASE_PUBLISHABLE_KEY. " +
    "Add them in your host's environment variables and redeploy."
  );
}

Change the names to match what your code already uses. The page is still blank, but the console now tells you exactly what’s missing.

Ask your AI tool

Paste this into Lovable, Bolt or Cursor. It asks the AI to find the problem without ever moving a secret into the browser:

My Vite + React app works in preview but shows a blank page after deploying to
[Vercel / Netlify / Cloudflare Pages / Cloudflare Workers]. The browser console
says: [paste the exact error].

1. List every environment variable the frontend reads via import.meta.env,
   with the file where each is used.
2. For each one, tell me if it is public (safe as VITE_) or secret.
3. If any secret key (Supabase secret/service_role, Stripe sk_ or whsec_,
   AI provider keys) is used in browser code, move that call into a
   server-side function instead. Never give a secret key the VITE_ prefix.
4. Add a clear error message if VITE_SUPABASE_URL or the Supabase
   publishable/anon key is missing.
5. Tell me exactly which variable names to add on my host. Don't ask me
   for the values and don't put real keys in code.

Test it like a visitor

  1. Open the live site in a private window and confirm the home page loads.
  2. Open the console again. No red errors.
  3. Sign in, load some data and save something. If sign-in links send you to localhost, see Supabase Confirmation Email Redirects to localhost? Here’s the Fix.
  4. Refresh on a sub-page like /dashboard. If that 404s, see the 404 on refresh guide.
  5. In DevTools, press Ctrl + Shift + F (Cmd + Option + F on a Mac) to search all loaded files, and search for sk_, sb_secret_, service_role and whsec_. Nothing should turn up. Then run through the pre-launch security checklist.

Official sources used: Vite: Env variables and modes · Vercel: Managing environment variables · Vercel: Managing deployments · Vercel: Bolt + Vercel Drop · Netlify: Environment variables · Netlify: Manage deploys · Netlify: Create deploys · Cloudflare Pages: Environment variables · Cloudflare Workers Builds: Configuration · Lovable: Deploying and hosting outside Lovable · Supabase: API keys · supabase-js source · Chrome DevTools: Open DevTools · Chrome DevTools: Search. Checked October 2026.

Want the whole launch path in one place?

The Ship It Kit bundles copy-paste prompts, step-by-step deploy guides and a pre-launch checklist for Lovable, Bolt, Cursor and Replit apps. One-time $19. Launch price: $19 $17 until November 14, 2026.

Get the Ship It Kit, $19$17 See what's inside

Spotted something out of date? Platforms change their screens often. The official docs linked above are always the source of truth.