In short
- v0 builds Next.js App Router apps and deploys them to Vercel. Databases come from the Vercel Marketplace: Supabase, Neon, Upstash and Vercel Blob.
- Every exported Server Action answers a direct POST request, even when no button calls it. Check the session and the record's owner inside each one.
- Connecting Supabase syncs 13 variables into your project, and only the URL and publishable key carry
NEXT_PUBLIC_. The secret key, the JWT secret and the Postgres connection string belong in server code, stored as Vercel Secret variables: Vercel's April 2026 incident exposed variables that were not marked sensitive. - Middleware alone is not authorization: Next.js CVEs in 2025 and 2026 let requests past it. The November 2026 security release is Next.js 16.4.5 or 15.5.29.
- A chat set to Anyone on the web may be indexed by search engines with its whole history. Check the visibility of any chat where you pasted a key, a schema or customer data.
On this page
Is v0 safe? The short answer
Yes, as a place to build. v0 is Vercel's AI app builder: you describe an app, it writes the code, runs it in a sandbox and publishes it to Vercel. Its security documentation says it treats all generated code as potentially incorrect or adversarial, analyzes it before execution and runs it in isolated environments. Once deployed, server code runs as Vercel Functions in isolated containers, environment variables are stored in what v0 calls Vercel's secure vault, and v0 is inside the scope of Vercel's SOC 2 Type 2 attestation.
The app you publish is another matter. v0's FAQ answers "Is my code secure?" with a yes that points to secure coding practices and Vercel's infrastructure. Infrastructure does not decide whether a stranger can call your deleteProject action with someone else's id, read a Supabase table, or run up your AI bill through /api/chat. Those few lines of authorization code are yours, and generated apps get them wrong most often.
What v0 and Vercel handle, and what you handle
| Area | v0, Vercel and Next.js | You |
|---|---|---|
| Generated code | Analyzed before it runs, executed in a sandbox | Reading the server code before you publish |
| Environment variables | Stored in Vercel's vault, write-only when typed Secret; only NEXT_PUBLIC_ variables reach the browser; v0 warns about risky NEXT_PUBLIC_ use | Choosing which variables carry the prefix, storing keys as Secret, and rotating any secret that was ever exposed |
| Server Actions and API routes | Hard-to-guess action ids, unused actions dropped from the bundle | Checking the session and the record's owner inside every action and route handler |
| Database | One-click provisioning and variables through the Vercel Marketplace | RLS, grants and policies in Supabase; owner filters in every Neon query |
| Framework CVEs | Routing that kept Vercel-hosted apps out of CVE-2025-29927; React2Shell patches pushed to v0 chats | Keeping next and react at the patched versions in package.json |
| Who sees what | Chat visibility, Deployment Protection, AI spend limits | Setting them: the chat's audience, the protection scope, the limits |
The stack v0 builds, and where it leaks
v0 defaults to Next.js with the App Router. Its docs say it uses React Server Components, creates backend endpoints with App Router conventions and connects to databases such as Supabase, Neon and Upstash without an ORM by default. Integrations come from the Vercel Marketplace, under Project menu > Settings > Integrations: Supabase, Neon, Upstash and Vercel Blob for data, Grok, fal and Deep Infra for AI. For SQL databases, v0 can generate and execute SQL itself, so it creates, changes and drops your tables.
| Part | What v0 generates | Where it leaks |
|---|---|---|
Server Actions ("use server") | Form handlers and mutations next to the UI | Anyone can POST to an exported action. Without a session and owner check, anyone can change anyone's data |
Route handlers (app/api/.../route.ts) | JSON endpoints, webhooks, AI chat routes | Same rule: no check means public. AI routes spend your credits for strangers |
middleware.ts or proxy.ts | Redirects to sign-in for protected paths | Used as the only lock, and CVEs have slipped requests past it |
| Supabase | Tables created by SQL, Supabase Auth, the publishable key in the browser | Tables without RLS that anon can read or write; policies that check sign-in but not ownership |
| Neon | DATABASE_URL used from server code | Every visitor shares one database role, so the database cannot tell users apart: every rule lives in your queries |
| Environment variables | Keys added by integrations and by you | A secret renamed with NEXT_PUBLIC_ ends up in the JavaScript every visitor downloads |
Sign-in deserves a sentence of its own. v0's docs do not describe a sign-in system of its own: you prompt for it, and the full-stack guide's example prompt is "Add user authentication so users can only see and manage their own tasks." The second half of that sentence is the part to verify. A sign-in page can work perfectly while the server answers anyone who calls it directly.
Concrete risks in v0 apps
Server Actions that skip the owner check
Next.js documents that an exported Server Action is reachable by a direct POST request, not only through your UI, even when no file imports it. Next.js makes action ids hard to guess and drops unused ones from the bundle, then says plainly that you should still verify authentication and authorization inside each action. The ids of the actions your pages use ship in the JavaScript. A generated updateTask(id, data) that runs update tasks set ... where id = $1 lets anyone who can send a request edit any task. That is an IDOR, and "signed in" checked without "owns this row" is the access control bug we find most often in generated apps.
Sign-in checked only in middleware
Generated apps often protect /dashboard with a redirect in middleware.ts (proxy.ts in recent Next.js versions) and nowhere else. Middleware is a good place for a redirect and a poor place for the only check. CVE-2025-29927 let one request header skip middleware on self-hosted Next.js; Vercel's postmortem explains that apps hosted on Vercel were never affected, because its routing runs in a separate system. CVE-2026-44575, rated 7.5 (high) in GitHub's advisory, let crafted .rsc and segment-prefetch URLs reach App Router pages without the middleware rule matching, in Next.js 15.2.0 up to 15.5.16 and 16.0.0 up to 16.2.5. That first fix was incomplete: a follow-up, CVE-2026-45109, closed the gap in 15.5.18 and 16.2.6. The lasting fix is the same for both: check the session again where the data is read.
Secrets one prefix away from the browser
Installing Supabase from the Vercel Marketplace syncs 13 environment variables into your project. Two carry the NEXT_PUBLIC_ prefix and are meant for the browser: NEXT_PUBLIC_SUPABASE_URL and NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY. Next to them sit SUPABASE_SECRET_KEY, SUPABASE_JWT_SECRET, POSTGRES_URL and POSTGRES_PASSWORD, none of which belong in a browser. Neon adds DATABASE_URL and its parts the same way. Browser code cannot read a variable without the prefix, so when a client component needs a value, the quickest fix an agent can find is to add the prefix. v0 analyzes NEXT_PUBLIC_ usage and warns you, which helps, but the warning only works if someone reads it. Our guide to API keys exposed in the frontend lists which keys may be public.
Where the values sit matters too. In April 2026, Vercel disclosed that an attacker who came in through a third-party AI tool used by one of its employees decrypted environment variables that a limited subset of customers had not marked as sensitive. Vercel advises reviewing and rotating every such value. Vercel now types each variable as Config, readable after saving, or Secret, write-only. Variables created by an integration, like the 13 from Supabase, cannot be switched to Secret in place: Vercel's steps are to disconnect the integration, rotate its secrets, reconnect it with Development unchecked and Sensitive turned on, then redeploy.
Supabase tables created by SQL
When Supabase is connected, v0 writes and runs the SQL that creates your tables. Supabase's own rule is short: a table in an exposed schema without RLS (row level security) is readable and writable by any role with a grant on it, and on projects that still grant by default, a new table in public starts with every privilege granted to anon and authenticated. The publishable key in your bundle acts as anon. If a create table ran without enable row level security, the table answers anyone. The Supabase RLS guide has the policies for the common cases.
There is a quieter case. Server code that queries through POSTGRES_URL connects as a database user, not as your visitor. Supabase's docs note that its postgres role has bypassrls, and PostgreSQL says a table's owner normally bypasses row security too. Check which user POSTGRES_USER names in your Vercel variables: if it is postgres or the owner of your tables, RLS protects nothing on that path, so the owner check has to sit in the query or in the action that runs it. The same goes for every query to Neon. The Supabase secret key also skips RLS, unless the request carries the signed-in user's access token.
AI routes anyone can call
v0 can connect AI providers such as Grok, fal and Deep Infra, and the setup lets you set spend limits and auto top-ups: set them. A route like /api/chat or /api/generate that calls the model without checking the session lets any visitor, or any script, use your key at your cost. The guide on unprotected AI endpoints covers the fix in depth.
Chats, previews and shared caches
A v0 chat holds your prompts and your code. Its visibility can be Private, your team, Anyone with the link (not indexed) or Anyone on the web, which may be indexed by search engines and appear in public galleries. New team chats are visible and editable by the whole team unless a team Owner changes the default, and Owners can open any team chat by its URL. If you pasted a key, a schema or customer data into a chat, check its audience in the Invite dialog.
For deployments, Vercel's Standard Protection covers every deployment except production domains, and All Deployments covers production too. v0 asks you to choose visibility at first publish. Last, the Next.js November 2026 security release, 16.4.5 and 15.5.29, fixes cache bugs that could serve one tenant's or one user's cached result to another in apps that use Cache Components. Our Next.js security guide covers the full CVE list.
How to check your v0 app
Start with what you can read in v0's code view or in your GitHub export, then test the live app signed out. Everything below runs against your own project.
- 1
Check the Next.js version
Open
package.json.nextshould be 16.4.5 or later on the 16 line, or 15.5.29 or later on 15.5: the versions set by the November 2026 security release. Paste the file into the Next.js CVE checker to see every CVE that applies to your versions of Next.js and React. - 2
List what reaches the browser
Search the code for
NEXT_PUBLIC_, then open Project menu > Settings > Environment Variables in v0. Only the Supabase URL, the publishable key and values you would happily print on your home page belong behind the prefix. Any secret there is already in your JavaScript. Then, in the Vercel dashboard, check that every key carries the Secret label. - 3
Find every server entry point
Search for
"use server"and list theroute.tsfiles underapp/api. For each exported function, find the line that reads the session and the line that checks the record belongs to that user. A function with neither is public. - 4
Read your middleware
Open
middleware.tsorproxy.ts. If it is the only place that checks sign-in for a page or an API route, add the check to that page's data loading and to the route itself. - 5
Check the database
With Supabase, open Security Advisor in the Supabase dashboard and run the query from the Supabase RLS checker. With Neon, every query in an action or route needs an owner filter built from the session, never from the request body.
- 6
Check who can see the chat and the deployments
Open the chat's Invite dialog. Then open Project menu > Settings > Vercel Project for production visibility, and the Deployment Protection settings in Vercel for previews.
# In the root of your exported v0 project
grep -rn "NEXT_PUBLIC_" app components lib --include=*.ts --include=*.tsx
grep -rln "use server" app components lib
find app/api -name "route.*"
grep -rln "SUPABASE_SECRET_KEY\|SUPABASE_SERVICE_ROLE_KEY\|SUPABASE_JWT_SECRET\|POSTGRES_URL\|DATABASE_URL" app components libimport 'server-only'. A match in a file marked "use client" is a leak.Then test one AI route the way a stranger would, with no cookie:
curl -i -X POST "https://YOUR-APP.vercel.app/api/chat" \
-H "content-type: application/json" \
-d '{"messages":[{"role":"user","content":"Say hi"}]}'401 is what you want.Fixes, with prompts for v0
v0 edits code from the chat, so the fastest fix is a precise prompt. Send these one at a time and read the diff before you publish.
Check the owner inside every action
// Before: anyone who can send a POST can delete any project
"use server";
import { sql } from "@/lib/db";
export async function deleteProject(id: string) {
await sql`delete from projects where id = ${id}`;
}
// After: the session and the owner are checked inside the action
"use server";
import { createClient } from "@/lib/supabase/server";
export async function deleteProject(id: string) {
const supabase = await createClient();
const { data: { user } } = await supabase.auth.getUser();
if (!user) throw new Error("Not signed in");
const { error } = await supabase
.from("projects")
.delete()
.eq("id", id)
.eq("owner_id", user.id);
if (error) throw error;
}sql but add and owner_id = ${user.id}, with the id taken from the session.Audit every file with "use server" and every route handler under app/api.
In each exported function: read the session on the server, return 401 or throw
if there is none, and check that the signed-in user owns the record before
reading, changing or deleting it. Take the user id from the session, never from
the request body. Validate the input inside the function with zod. Do not rely
on middleware or on hidden buttons. List every function you changed.Keep secrets on the server
List every environment variable this app reads and where. Only the Supabase URL
and the publishable key may use the NEXT_PUBLIC_ prefix. Move every use of
SUPABASE_SECRET_KEY, SUPABASE_JWT_SECRET, POSTGRES_URL, DATABASE_URL and AI
provider keys into Server Actions, route handlers or modules that start with
import "server-only". Never add NEXT_PUBLIC_ to fix an undefined value.
Tell me which variables were ever public so I can rotate them.Lock the Supabase tables
alter table public.tasks enable row level security;
revoke all on table public.tasks from anon, authenticated;
grant select, insert, update, delete on table public.tasks to authenticated;
create policy "tasks_select_own" on public.tasks
for select to authenticated using ((select auth.uid()) = user_id);
create policy "tasks_insert_own" on public.tasks
for insert to authenticated with check ((select auth.uid()) = user_id);
create policy "tasks_update_own" on public.tasks
for update to authenticated
using ((select auth.uid()) = user_id) with check ((select auth.uid()) = user_id);
create policy "tasks_delete_own" on public.tasks
for delete to authenticated using ((select auth.uid()) = user_id);public, then re-run Security Advisor.Protect AI routes and upgrade Next.js
Every route under app/api that calls an AI model must check the session and
return 401 without one, cap the prompt length, and limit requests per user.
Then upgrade next to 16.4.5 (or 15.5.29 if the app is on 15.5), update react
and react-dom to their latest patch release, and run a production build.The traps after the first fix
- Removing the prefix without rotating. Deleting
NEXT_PUBLIC_fixes the next build, not the key that already shipped. Rotate it at the provider, then update the variable in Vercel. - Signed in is not the owner. A check like
if (!user) throwstops strangers only until they sign up. If sign-up is open, a stranger is one form away from passing it, so compare the record's owner withuser.id. - Validation only in the form. A schema checked in a client component does nothing for a direct POST. Parse the input again inside the action.
- HTML from the model. React escapes what it renders, except through
dangerouslySetInnerHTML. Chat apps that render AI answers or user Markdown as HTML need a sanitizer. - No Content Security Policy. Next.js does not send one unless you configure it, and a CSP is what limits the damage when a script injection slips through. Add headers in
next.configand run the security headers checker on your live address. - Previews on production data. Vercel sets each variable per environment: Production, Preview, Development. If previews point at the production database, a preview with protection off is a second public copy of your app on real data. Neon can create a separate database branch for each preview.
What Keybrum tests on a live v0 app
Reading code finds the missing check. An attack shows what a stranger can actually do with it. On a published v0 app whose owner proved ownership, Keybrum:
- reads the JavaScript for secrets, from Supabase secret keys to AI, Stripe and database credentials, and checks whether each one works;
- tries the Supabase tables, functions and buckets the app names, as
anonand as a freshly signed-up account; - creates a marked record with one test account, then asks for it with a second account and with none, to prove an IDOR;
- replays pages that redirect to sign-in with the CVE-2025-29927 header, as router prefetches and through path variants;
- tests Server Actions for the
Origin: nullCSRF bypass (CVE-2026-27978) and the Host header SSRF (CVE-2024-34351), and reports the React Server Components patch level without attempting exploitation; - sends a five-word prompt to routes named like AI endpoints, and checks source maps, exposed
.envfiles and security headers.
Every finding comes with the request that worked and the fix, which your coding agent reads through Keybrum's MCP server. Keybrum proves a CVE by its behaviour, never from a version number, and attacks again after your fix.
v0 security checklist
nextis at 16.4.5 or later (or 15.5.29 on 15.5), andreactis on its latest patch- Only the Supabase URL, the publishable key and truly public values use
NEXT_PUBLIC_ SUPABASE_SECRET_KEY,SUPABASE_JWT_SECRET,POSTGRES_URLandDATABASE_URLappear only in server code- Keys are stored as Vercel Secret variables, and keys that were not marked sensitive in April 2026 have been rotated
- Every secret that ever carried the prefix has been rotated
- Every exported Server Action checks the session and the record's owner
- Every route handler under
app/apidoes the same, AI routes included - Pages behind middleware check the session again where they load data
- Every Supabase table in
publichas RLS on, minimal grants and owner-based policies - Queries through
POSTGRES_URLor Neon filter by the signed-in user's id - Input is validated again inside each action, not only in the form
- AI providers have spend limits, and AI routes have per-user rate limits
- Chats holding keys or customer data are private, and previews are protected
- The live app was tested signed out and as a second account after the last deploy
For the same audit across every AI builder, see the vibe coding security checklist and the vibe coding security risks behind it.
Questions
Is v0 safe to use?
Yes, as a builder. v0 analyzes generated code before running it in sandboxes, and it is in the scope of Vercel's SOC 2 Type 2 attestation. The app it builds still needs authorization checks in its Server Actions, routes and database, and v0 does not verify those for you.
Is v0 generated code secure?
It follows Next.js conventions, which helps: React escapes what it renders, only NEXT_PUBLIC_ variables reach the browser, and v0 warns about risky prefixes. The usual gaps are actions and routes without an owner check, middleware used as the only lock, and Supabase tables without RLS. Read the server code before you publish.
Does Vercel protect my v0 app from Next.js CVEs?
Sometimes. Apps hosted on Vercel were not affected by CVE-2025-29927 because of how Vercel runs routing, and Vercel pushed React2Shell patches to v0 chats while telling owners to patch right away. Other CVEs need the upgrade in your package.json: the November 2026 security release is Next.js 16.4.5 or 15.5.29.
Can other people see my v0 chats?
Only if the visibility allows it. Anyone with the link opens the chat to whoever has the URL, and Anyone on the web may get it indexed by search engines. Team chats are open to the whole team by default, and Vercel team Owners can open any team chat by its URL.
Are environment variables in v0 safe?
Variables without the NEXT_PUBLIC_ prefix stay on the server, and variables with it are written into the JavaScript every visitor downloads. Store keys as Vercel Secret variables, which are write-only after saving: in April 2026 an attacker decrypted variables that a limited subset of Vercel customers had not marked sensitive. Read secrets only in Server Actions, route handlers or server-only modules.
Does v0 scan my app for vulnerabilities?
v0's documentation describes a security analysis of generated code before it runs and warnings about NEXT_PUBLIC_ usage. Neither sends requests to your live app the way a stranger would. The free check shows the outside view in about ten seconds.
Sources
- 1.Security, v0 Docs
- 2.Full-stack apps, v0 Docs
- 3.Databases, v0 Docs
- 4.Sharing, v0 Docs
- 5.Deployments, v0 Docs
- 6.FAQs, v0 Docs
- 7.Vercel April 2026 security incident, Vercel
- 8.React2Shell security bulletin, Vercel
- 9.Postmortem on Next.js Middleware bypass, Vercel
- 10.Config and Secret environment variables, Vercel Docs
- 11.Deployment Protection, Vercel Docs
- 12.How to think about data security in Next.js, Next.js Docs
- 13.November 2026 Security Release, Next.js Blog
- 14.CVE-2026-44575, NIST National Vulnerability Database
- 15.Middleware / Proxy bypass via segment-prefetch routes, incomplete fix follow-up (CVE-2026-45109), GitHub Advisory Database
- 16.Vercel Marketplace, Supabase Docs
- 17.Row Level Security, Supabase Docs
- 18.Vercel native integration, Neon Docs
- 19.Row Security Policies, PostgreSQL documentation