GUIDESEPISODES
Works on localhost, broken after deploy? 4 fixes
Your app ran perfectly on your laptop. You deployed it, and the live site is a blank page. Nothing is cursed: your app moved to a new house and four things stayed behind. Claude runs the move, one box at a time, each step checked against the Vercel, Next.js and Supabase docs.

01STEP 1 · THE NOTE ON THE DOOR
Read the real error before you touch anything
Why it mattersThe error message is the shortest path to the fix. Guessing is the longest.
Here’s the story. Your app ran perfectly on your laptop. You deployed it to Vercel, opened the link, and got a blank white page.

Open the full chapterIncludes a table, code, a note
The first move is not “fix it”. It’s finding out what broke. Telling your AI “it’s broken” makes it guess, and guesses rewrite code that was fine.
Where the real error lives
| The page… | Look here | How |
|---|---|---|
| Loads but is blank or half-broken | The browser console | Open the live site, press F12 (or right-click → Inspect), then the Console tab. Copy the first red line |
| Never went live (the deploy failed) | The Vercel Build Logs | Vercel: “click the Build Logs button from the production deployment tile in the projects overview page.” Copy the lines in red near the end |
| Loads, but an action fails (login, checkout) | The console and the Network tab | Do the action with the console open, then copy the red line and the failing request’s status code |
Then paste it to Claude, word for word:
My Vercel deploy shows a blank page. Here is the exact red line from the
browser console (F12 → Console):
<paste the red line here>
And here are the last 20 lines of the Vercel Build Logs:
<paste them here>
Explain what it means before you change anything.Exact error in, exact answer out. The last line stops Claude from editing before you both know the cause.
02STEP 2 · THE KEYS BOX
Move your keys to Vercel, not to GitHub
Why it mattersYour laptop and the server each need their own copy of the keys. GitHub needs none.
The most common blank page has one cause: the live site has no keys. Your keys live in .env.local, and that file never left your laptop.

Open the full chapterIncludes code, a table, a note
That part is correct. Next.js’s create-next-app template already lists .env files in .gitignore, because “you almost never want to commit these files.” Your keys should never be on GitHub.
# .env.local (stays on your laptop: create-next-app already lists .env files in .gitignore)
NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key
SUPABASE_SERVICE_ROLE_KEY=your-service-role-key
STRIPE_SECRET_KEY=your-stripe-secret-keyExample names only. Your app’s names are whatever your code reads after process.env.
So the server needs its own copy. In Vercel, open your project → Settings → Environment Variables. Add each key with the same name, and tick Production.
Where each copy lives
| Place | What it’s for | Goes to GitHub? |
|---|---|---|
.env.local on your laptop | npm run dev on your machine | No. Keep it in .gitignore |
| Vercel → Settings → Environment Variables (Production) | The live site | No. Vercel stores it |
| Your code | Reads the name, like process.env.STRIPE_SECRET_KEY | Yes, but only the name, never the value |
03STEP 2 · THE LIGHT SWITCH
Added the keys and still blank? Redeploy
Why it mattersA refresh shows the old build. A redeploy builds a new one with the keys inside.
You added every key in Vercel. You refresh the site. Still blank. This one catches almost everyone.

Open the full chapterIncludes steps, a note
Vercel’s docs: “Any change you make to environment variables are not applied to previous deployments, they only apply to new deployments.” The site you’re looking at was built before the keys existed.
Two ways to redeploy
- In Vercel, open Deployments, open the menu on the latest production deployment and choose Redeploy.
- Or push any new commit to the branch Vercel deploys from. The new build picks up the keys.
Still blank after the redeploy? Go back to step 1 and read the new red line. It is usually a different error now, which means you’re one box further.
04STEP 2 · THE SHOP WINDOW
A secret key never gets NEXT_PUBLIC_
Why it mattersThe prefix doesn’t fix a missing key. It publishes it.
Here’s the trap. The page is still blank, someone online says “add NEXT_PUBLIC_ to the name”, and it works. It works because now the key is in every visitor’s browser.

NEXT_PUBLIC_ puts the key in the shop window. Claude takes it back inside.Open the full chapterIncludes code, a table, a note
Next.js copies every NEXT_PUBLIC_ value “into any JavaScript sent to the browser.” Anyone can open DevTools and read it. Variables without the prefix “are only available in the Node.js environment, meaning they aren’t accessible to the browser.”
'use client'
// NEXT_PUBLIC_ = copied into the JavaScript every visitor downloads
const stripeKey = process.env.NEXT_PUBLIC_STRIPE_SECRET_KEY// app/api/checkout/route.ts (runs on the server only)
export async function POST(request: Request) {
const stripeKey = process.env.STRIPE_SECRET_KEY // no prefix: never sent to the browser
// ...call Stripe here and return only what the page needs
}Which keys may be public
| Key | Prefix? | Why |
|---|---|---|
| Supabase project URL and anon (publishable) key | NEXT_PUBLIC_ is fine | Made for the browser. Your Row Level Security policies decide what it can read |
| Supabase service role key | Never | It skips Row Level Security. Server code only |
| Stripe secret key, OpenAI or other API secrets | Never | Anyone who reads it can spend your money |
Vite apps: anything with VITE_ | Same rule | Vite exposes VITE_ variables to the browser code, just like NEXT_PUBLIC_ |
05STEP 3 · THE MAILBOX
Login emails go to localhost? Change the address
Why it mattersYour app moved. Its login emails need the new address too.
The site loads, you sign up, the confirmation email arrives, and the link opens localhost:3000. On a phone, that’s a dead page.

Open the full chapterIncludes steps, code
Supabase Auth decides where those links go. Its Site URL “defines the default redirect URL when no redirectTo is specified in the code,” and it starts as http://localhost:3000.
Set the new address
- In Supabase, open Authentication → URL Configuration.
- Set Site URL to your live address, like
https://yourapp.com(or your.vercel.appaddress if you have no domain yet). - Under Redirect URLs, add your live address too, plus any callback path your app uses, like
https://yourapp.com/auth/callback. - Keep
http://localhost:3000in the Redirect URLs list if you still test on your laptop.
Does your code pass a redirectTo or emailRedirectTo? Supabase checks it against that list. The docs: “The URL in redirectTo should match the Redirect URLs list configuration.” Build it from the current address, not a typed-in localhost:
const { error } = await supabase.auth.signUp({
email,
password,
options: {
// must match an entry in Supabase → Authentication → URL Configuration → Redirect URLs
emailRedirectTo: `${window.location.origin}/auth/callback`,
},
})In server code there is no window: read your site address from an environment variable instead.
Then find the other old addresses
grep -rn "localhost" . --exclude-dir=node_modules --exclude-dir=.next --exclude-dir=.gitEvery hit in app code is a link that only works on your laptop. Swap it for a relative path or an environment variable.
06STEP 4 · THE LABELS
“Module not found” on Vercel? Match the capital letters
Why it mattersYour Mac forgives capital letters. The server reads them exactly.
The build fails with Module not found, but the file is right there. Look closely at the capital letters.

Open the full chapterIncludes code, a note
Here’s Vercel’s explanation. “Windows and macOS use case-insensitive filesystems by default, so Header.tsx and header.tsx point to the same file on your machine.”
The server is different. “Vercel builds run on a case-sensitive Linux filesystem, where those are two different paths.”
// file on disk: components/header.tsx
import Header from '@/components/Header' // works on your Mac, "Module not found" on Vercel// file on disk: components/header.tsx
import Header from '@/components/header' // matches the file name letter for letterRenaming the file instead
If you’d rather keep Header with a capital H, rename the file. On a Mac, Git often doesn’t notice a rename that only changes capital letters, so go through a temporary name, as Vercel suggests:
git mv components/header.tsx components/header-tmp.tsx
git mv components/header-tmp.tsx components/Header.tsx
git commit -m "Rename header.tsx to Header.tsx"
git pushTwo moves, one commit. Git records the real new name, and the next build finds it.
07MOVED IN
The whole move on one page
Why it mattersMost broken deploys are one of four boxes left behind.
Keep this next to your deploy button.

Open the full chapterIncludes a table, a note
| What you see | Likely cause | The fix |
|---|---|---|
| A blank page, no idea why | Unknown yet | Step 1: copy the red line from the console or the Build Logs |
| “undefined” where a key should be | Keys only in .env.local | Step 2: add them in Vercel → Settings → Environment Variables |
| Added the keys, still broken | Old deployment | Redeploy: new keys only reach new deployments |
| Someone says “add NEXT_PUBLIC_” | A secret about to go public | Keep secrets unprefixed and in server code |
| Login email opens localhost | Supabase Site URL | Step 3: set the Site URL and Redirect URLs |
Module not found, but the file exists | Capital letters | Step 4: match the import, or git mv through a temp name |
Then paste the prompt from the top of this page into Claude Code. It asks you for the real error, compares your code with your Vercel settings, and shows you every finding with file and line before it edits anything.
THE QUICK READ
What to take away.
In one minute.
- 01
“Works on my laptop” means it works on your laptop. Claude Code builds and tests there, so that is where it says “done”.
- 02
Start with the real error: F12 → Console in the browser, and the Build Logs on the Vercel deployment. Paste the exact red line, not “it’s broken”.
- 03
.env.localstays on your laptop. Add each key again in Vercel → Settings → Environment Variables. - 04
Then redeploy. Vercel: changes to environment variables “only apply to new deployments.”
- 05
A secret key never gets
NEXT_PUBLIC_. Next.js copies those values into the JavaScript every visitor downloads. - 06
Supabase’s Site URL starts as
http://localhost:3000. Until you change it, login emails can send people to localhost. - 07
Your Mac ignores capital letters in file names. Vercel’s Linux build doesn’t:
Headerandheaderare two different files there.
THE WHOLE MOVE, IN ONE PASTE
Paste this. Let Claude find what stayed behind.
Paste this into Claude Code, Cursor or any coding agent. It investigates first, asks you for the real error, and shows you its findings with file and line before it edits anything.
My app works on localhost but is broken after I deploy it (blank page or errors). Investigate before you change anything: 1. Ask me for the exact error from the browser console (F12 → Console) and from the Vercel Build Logs. Don't guess. 2. List every environment variable the code reads (process.env.*, import.meta.env.*). Compare it with what I set in Vercel → Settings → Environment Variables for Production and tell me what's missing. Remind me to redeploy after adding them. 3. Flag any secret key (service role, API secret, Stripe secret) that has a NEXT_PUBLIC_ or VITE_ prefix or is used in client code. Those ship to every browser: move them to server-only code. 4. Find hard-coded localhost URLs and auth redirects, and tell me what to set in Supabase → Authentication → URL Configuration (Site URL and Redirect URLs). 5. Check that every import path matches the file name's exact capital letters. For a case-only rename, use git mv through a temp name. Show me the findings as a list (file and line) before you edit anything.
Paste it into the agent you already build with
- Claude
- ChatGPT
- Gemini
- Cursor
- Copilot
QUESTIONS PEOPLE ASK
Good questions.
Short answers.
Why does my app work on localhost but not after I deploy to Vercel?
Your laptop has things the server doesn’t: your .env.local keys, your local address and a file system that ignores capital letters. Start with the exact error in the browser console or the Vercel Build Logs, then check those three.
Do I need to redeploy after adding environment variables on Vercel?
Yes. Vercel’s docs say changes to environment variables “only apply to new deployments.” Redeploy from the Deployments tab or push a new commit.
Should I commit .env.local to GitHub?
No. Next.js’s create-next-app template adds .env files to .gitignore for that reason. Keep the file on your laptop and add the same keys in your host’s environment variable settings.
Is it safe to put an API key in a NEXT_PUBLIC_ variable?
Only if the key is meant to be public, like a Supabase project URL and anon key. Next.js copies NEXT_PUBLIC_ values into the JavaScript sent to every browser. Secrets like a Stripe secret key or a service role key must stay unprefixed and in server code.
Why do my Supabase login emails link to localhost?
Supabase uses the Site URL as the default redirect, and it starts as http://localhost:3000. Set your live address under Authentication → URL Configuration, and add it to the Redirect URLs list.
Why does Vercel say “Module not found” when the file exists?
Usually capital letters. macOS and Windows treat Header.tsx and header.tsx as the same file; Vercel’s Linux build does not. Make the import match the file name exactly, or rename it with git mv through a temporary name.
Does Aster check my Vercel or Supabase settings?
No. Aster reads your GitHub repository and flags secret keys committed in your code, including ones behind NEXT_PUBLIC_ or VITE_. Your host’s environment variables, Supabase URL settings, build logs and import names are yours to check with this guide.
Read the note, move the keys, forward the mail, match the labels. Before you deploy again, check that no secret key went public: your first repository audit is free, and Aster flags secret keys committed in your code, including ones behind NEXT_PUBLIC_ or VITE_.
Source notebook5 links
- Vercel Docs: Accessing build logs (Build Logs button on the production deployment)
- Vercel Docs: Environment variables (changes only apply to new deployments)
- Next.js Docs: How to use environment variables (NEXT_PUBLIC_ is inlined at build time)
- Supabase Docs: Redirect URLs (Site URL and the Redirect URLs list)
- Vercel Knowledge Base: How do I resolve a “module not found” error? (case-sensitive builds)










