Get an engineer for this one. Server code, auth and environment variables are developer work.
- How serious
- High
- Fix it yourself?
- No, get an engineer
- Typical cost
- $1,500–$15,000 cleanup and hardening (prices)
- Top rated for v0
- Afterbuild Labs · 10.0/10
A v0 app is a Next.js project on Vercel, so it already has a place to run server code. What it usually lacks before real users is the server side of the logic: access checks in every server action and route handler, secrets kept out of NEXT_PUBLIC_ variables, a database with access rules, and limits on AI calls.
What you see, and what it usually means
| What you see | What it usually means |
|---|---|
| The screens look finished but every button only changes the page. | There is no server logic or database yet. The UI is a prototype. |
| Data saves in the v0 preview and is gone on the published URL. | The preview uses Development variables. Production has none set, or a different database. |
| An API key is visible in the page source. | It was put in a NEXT_PUBLIC_ variable, which is built into the browser JavaScript. |
| Any signed-in user can call an admin action by guessing the request. | The server action has no check on who is calling it. |
| The AI feature works for you and runs up a bill on launch day. | No per-user limit on the route that calls the model. |
Why it happens
v0 publishes to a Vercel project connected to the chat, with preview deployments per branch and a stable production URL. Environment variables are set on that Vercel project for Production, Preview and Development, and the v0 preview can read only the Development ones. A change to a variable applies only to new deployments. So a key that works in the preview can be missing on the live site until you set it for Production and publish again.
Next.js builds any variable that starts with NEXT_PUBLIC_ into the JavaScript sent to the browser. That is correct for a Supabase publishable key and wrong for anything secret. The other gap is authorization. The Next.js docs say to treat server actions and route handlers with the same security considerations as public-facing API endpoints, and that hiding a button is not protection, because an app has many entry points. The AI generates the button, the action, and the database call, and rarely the check in between.
Check these first
Admin actions are checked on the server
Hiding an admin button is not protection. Call the admin endpoint as a normal user and confirm it is refused.
No secret keys reach the browser
Open your live app, then DevTools, Sources, and search the loaded JavaScript for sb_secret_, sk_live_, sk-proj- and sk-ant-. Older Supabase keys are long tokens starting with eyJ: decode the middle part of each on your own machine, and if it contains "role":"service_role", your secret key is public. A publishable or anon key in the browser is expected.
Input is validated on the server
Forms that only check input in the browser accept anything sent directly to the API.
Access rules are on for every table
In Supabase, open Table Editor: every table should show RLS enabled, with at least one policy. In Firebase, your rules should never say allow read, write: if true.
Secrets live in the host's environment settings
Keys belong in environment variables on the server, not in the repository or in front-end code.
AI and paid API calls have per-user limits
Without a cap, one user or one bot loop can spend your monthly budget in an hour.
Fix it yourself
- In v0, open Project menu → Settings and look at Environment Variables. For each one, ask: is it set for Production as well as Development, and does the name start with
NEXT_PUBLIC_only if the value is safe to show everyone? - Open your published site, press F12, choose Sources, and search the JavaScript for
sk_,sb_secret_andwhsec_. Anything found must be rotated and moved to a server-only variable. - Ask v0 in chat: "List every server action and route handler, and show me the line in each where it checks the signed-in user before reading or writing data." Any file without that line is open to anyone.
- If you use Supabase, open the Table Editor and confirm RLS is enabled with a policy on every table. Supabase returns no rows through the publishable key until a policy exists, which is the safe failure.
- Ask v0 to add a per-user limit to any route that calls an AI model, and to read prices and plan names on the server rather than from the request.
- Connect the project to GitHub from v0 so publishes go through a pull request you can read. After any variable change, publish again, because old deployments keep the old values.
When to get a human engineer
Get an engineer before launch when the app takes payments, stores personal data, or calls an AI model on your account. Adding access checks, moving secrets and wiring the database with rules is cleanup in the $1,500 to $15,000 band over one to eight weeks, depending on how many screens exist. A one-off check at $49 to $800 first will tell you whether the v0 code is a good base or a mockup to rebuild behind. Ask for the checks in our v0 guide as the deliverable.
Top-rated companies for v0 apps
Ranked by our score: published prices, scope of work, AI tools named, timing and a low-risk first step. How we score.
- 1Afterbuild Labs10.0/10Best for: A check before launchFree first step · from $49
- 2Redlab10.0/10Best for: A full rescue by one team that keeps buildingFree first step · from $490
- 3Vibe Code Rescue (Lev Arakelyan)9.0/10Best for: A one-off check and a cleanupFrom $299
Questions
Is v0 code production-ready?
The UI is often a good start. What is missing is behind it: access checks in server actions and route handlers, secrets out of the browser, validation on the server, and limits on paid calls. Those are the work before real users.
Why does my v0 app work in the preview but not on the published URL?
The preview reads only Development environment variables. Set the same variables for Production in the Vercel project and publish again, since changes apply only to new deployments.
Do I need Supabase with v0?
You need some database and sign-in provider. Supabase is common and works well if RLS is on for every table. Whatever you choose, the server actions still need their own check on who is calling.
Sources: v0 docs: Deployments; v0 docs: External APIs and environment variables; Next.js docs: Environment variables and NEXT_PUBLIC_; Next.js docs: Authentication, server actions and route handlers; Vercel docs: Environment variables.
