v0 app security: what a generated Next.js app can leak
v0 generates Next.js and React code with shadcn/ui components and deploys it to Vercel. Next.js gives you a real server, which is good news for security, as long as the secrets actually stay there. Here is where v0 apps tend to slip and what Shypset can see from outside.
The server is there, but is it used?
Unlike a pure single-page app, a Next.js project can keep keys in server components, route handlers and server actions, where the browser never sees them. The risk is the boundary. Any environment variable named NEXT_PUBLIC_… is inlined into the client bundle at build time. When a generated component needs a key and fails, renaming the variable to NEXT_PUBLIC_ makes the error disappear, and the key with it, straight into public JavaScript.
The same boundary matters for data. If the app uses Supabase or Firebase directly from client components, the database rules carry all the weight, just as in any frontend-only app.
What we often find
- Secret keys behind a
NEXT_PUBLIC_name: payment, email or AI provider keys. - No Content-Security-Policy. Vercel does not add one by default; you set it in
next.configorvercel.json. - Production source maps that were turned on for debugging and never turned off.
- A Supabase project URL in the client with tables that answer anonymous reads.
- Preview deployments shared publicly that point at production data.
What Shypset checks on a v0 app
We identify v0 builds from the v0 generator meta tag, v0 preview domains, or v0 together with shadcn/ui component signatures. Plain text that mentions v0.dev does not count.
The Public checks read the page and its scripts like a browser and look for leaked keys, source maps, client-side Supabase and Firebase configuration, response headers including the CSP, TLS, DNS and email records. With a verified domain we also probe referenced Supabase tables with the anon key and check for exposed files like /.env.production.
Shypset is a passive external check. It does not log in to your app and does not test application logic such as SQL injection or IDOR, so a clean report means nothing is visible from outside, not that the app is secure.
Fixing it in v0
Ask v0 to move every call that needs a secret into a route handler or server action, and to rename the variable without the NEXT_PUBLIC_ prefix. Rotate the old key first. Add security headers through the headers() function in next.config, starting with a CSP in report-only mode if you are unsure what it would break. Redeploy and rescan.
Example finding
A Public finding on a v0 app and the fix prompt we would give you.
Evidence
File: /_next/static/chunks/app/checkout/page-81c2.js Match: sk_live_51H…[masked]…k2Pq
Fix prompt
This v0 Next.js app exposes the Stripe secret key through NEXT_PUBLIC_STRIPE_SECRET_KEY. I have rolled the key in Stripe. Move the checkout session creation into a route handler at app/api/checkout/route.ts that reads STRIPE_SECRET_KEY on the server, update the checkout page to call it, and make sure no NEXT_PUBLIC_ variable contains a secret.
Questions
- Are NEXT_PUBLIC_ variables secret?
- No. Next.js writes them into the JavaScript sent to every visitor. Use them only for values that are public by design.
- Does Shypset work on preview URLs?
- It scans any public URL. Deeper Verified checks need a domain you can verify, which is usually your production domain.
Scan your v0 app
Paste the URL of your Vercel deployment on the home page and see what a visitor’s browser can see.
Scan your v0 app