Skip to content
StackSiren

v0 app security checklist: secrets, headers and previews

By StackSiren · Published

v0 apps are Next.js projects that usually deploy to Vercel. Before you share a link, keep secrets out of NEXT_PUBLIC_, send security headers from vercel.json or next.config, put privileged calls in Route Handlers, and turn on Deployment Protection for previews.

v0 generates UI and app structure quickly; it does not replace a launch pass. Work through each section on the production URL (and once on a preview). Only test projects you own. For a broader editor-agnostic list see the Cursor app security checklist.

1. NEXT_PUBLIC_ and secrets

Next.js inlines every NEXT_PUBLIC_ value your client code uses into the browser bundle at build time. Full detail: is NEXT_PUBLIC_ safe?.

Check

  1. In the Vercel project → Settings → Environment Variables, list every NEXT_PUBLIC_ entry.
  2. On the live site, DevTools → search sources for sk-, sk_live, sk-proj-, sb_secret_, service_role, OPENAI, ANTHROPIC.
  3. Confirm model keys, Stripe secret keys, webhook secrets and database URLs use unprefixed names and are only read in Server Components, Server Actions or Route Handlers.
// app/api/chat/route.ts — server only
export async function POST(req: Request) {
  const apiKey = process.env.OPENAI_API_KEY; // no NEXT_PUBLIC_
  if (!apiKey) return Response.json({ error: "missing key" }, { status: 500 });
  // call the provider, return only the result
}

Pass: only publishable values in the client (pk_…, Supabase anon / publishable, public analytics). Fail: a secret in the bundle — rotate first (exposed API keys), then move the call server-side. Our exposed API key checker crawls sites you own.

Also confirm /.env and /.env.local are not reachable (exposed env files, env file checker).

2. Headers

Vercel does not invent a full security-header set for you. Add them in vercel.json or next.config and verify with curl -I. Copy-paste starters: add security headers on Vercel. Guide: security headers.

curl -sI https://yourapp.com/ | grep -iE 'strict-transport|x-content-type|x-frame|referrer-policy|permissions-policy|content-security'

Minimum bar: Strict-Transport-Security, X-Content-Type-Options: nosniff, clickjacking protection (X-Frame-Options or CSP frame-ancestors), and a sensible Referrer-Policy. Roll CSP out in Report-Only first. Re-check the custom domain, not only *.vercel.app. Use the security headers checker on URLs you own.

3. Route Handlers and Server Actions

Anything privileged belongs behind a server boundary:

Do Don't
Call Stripe / OpenAI / Resend from app/api/…/route.ts or a Server Action Call secret-backed APIs from a "use client" component with a browser key
Authenticate the caller before side effects Trust a client-supplied userId without checking the session
Validate body size and shape Forward raw client JSON to a privileged API
Rate-limit sensitive routes where you can Expose debug handlers that dump process.env
// app/api/checkout/route.ts
import { auth } from "@/auth"; // your auth helper

export async function POST(req: Request) {
  const session = await auth();
  if (!session?.user) return new Response("Unauthorized", { status: 401 });

  const secret = process.env.STRIPE_SECRET_KEY;
  if (!secret) return new Response("Server misconfigured", { status: 500 });

  // create Checkout Session with Stripe, return { url } only
}

Never pass an unprefixed secret into a Client Component as a prop — Next.js will serialise it to the browser.

4. Previews

Vercel preview URLs are easy to share and, by default, often reachable by anyone with the link. Vercel Deployment Protection (Project → Settings → Deployment Protection) controls who can open preview and production URLs.

From Vercel's docs (verify labels in your dashboard):

  • Standard Protection — recommended for most teams: protects deployments except production domains (Vercel Authentication and other methods available by plan).
  • All Deployments — locks preview and production URLs behind authentication.
  • Methods include Vercel Authentication (team members), plus plan-gated Password Protection, Passport and Trusted IPs.

Before a Product Hunt, client review or public tweet:

  1. Enable at least Standard Protection on the project (or password-protect previews if your plan allows).
  2. Run the secrets and headers checks on one preview URL, then again on production after promote.
  3. Remember NEXT_PUBLIC_ values are baked at build time per deployment — a leaked preview build still contains whatever was inlined then.

Hobby vs Pro feature availability for Password Protection changes over time; check Vercel's current Deployment Protection pricing if you need passwords rather than Vercel logins.

Environment hygiene on Vercel

v0 projects inherit Vercel’s Environment Variables model: Development, Preview and Production can differ.

  • Put secrets in Preview and Production without NEXT_PUBLIC_ when only servers need them.
  • Mark highly sensitive values as Sensitive in the Vercel UI when available so dashboard readers cannot re-read them after creation.
  • Remember inlined NEXT_PUBLIC_ values are frozen per build — rotating a public-prefixed leak requires a redeploy of every deployment that still matters (including old previews you leave open).
  • Do not put secrets in next.config env — those are always bundled for the client.
# After a suspected leak: confirm the new build no longer contains the old material
SITE=https://yourapp.com
curl -s "$SITE/" | grep -oE '/_next/static/[^"]+\.js' | sort -u | while read -r js; do
  curl -s "$SITE$js" | grep -E 'sk-proj-|sk_live_|sb_secret_' && echo "hit in $js"
done

Launch day order of operations

  1. Secrets audit (NEXT_PUBLIC_ list + bundle search).
  2. Headers in config + curl -I on the custom domain.
  3. Route Handlers for every privileged provider call; remove debug routes.
  4. Deployment Protection on previews (Standard Protection at minimum).
  5. Auth + RLS / Storage checks if you use Supabase.
  6. Free StackSiren scan of the production URL you own; Solo ($29/mo) if you want scheduled rescans after each ship.

This order catches the failures that show up in Product Hunt and client demos most often: a key in the bundle, a wide-open preview, and missing headers on the custom domain.

CORS on Route Handlers

If a browser on another origin calls your v0 API with cookies, do not reflect arbitrary Origin values. Use an allowlist and read Is Access-Control-Allow-Origin: * safe? (25 Oct). Same-origin fetch('/api/...') from your own pages usually needs no ACAO at all.

FAQ

Does v0 set security headers for me? No. Generated Next.js code deploys like any other Vercel app. You add headers in config and confirm with curl -I.

Is the Supabase anon key OK in NEXT_PUBLIC_? Yes if it is the publishable/anon key and RLS (and Storage policies) are correct. The service_role / secret key never belongs in the client.

Should I protect production with Deployment Protection? Usually production stays public for a marketing site or SaaS. Protect previews so unfinished builds and debug flags are not indexed or casually opened. Use All Deployments only when the whole site must stay private.

I used Cursor and v0 together — which checklist? Both. This post is Vercel/Next-specific; the Cursor checklist covers host-agnostic items (.git, CORS, auth). Run both on the live URL.

What does Solo include? StackSiren's first scan of a site you own is free. Solo is $29/mo for ongoing scans and history when you want a schedule instead of one-off checks.

Do headers fix a leaked key? No. Rotate the key, remove it from NEXT_PUBLIC_, redeploy, then keep headers as a separate layer for browsers.

Check your app now

Free passive scan, results in under a minute.

Run a free scan

Fix guides for this topic

Platform checklists: v0 · Vercel · Cursor

Related posts

Sources