Vercel Deployment Security: The Production Checklist for Next.js
Vercel makes deploying trivial — and makes a few security footguns trivial too. Here's the production checklist: env scoping, headers, preview protection, and the bundle leak everyone hits.
Easy to deploy, easy to leak
Vercel's developer experience is excellent, which means the security mistakes are also easy to make at speed. This checklist covers the Vercel-and-Next.js-specific ones.
1. The NEXT_PUBLIC_ bundle leak
Any env var prefixed NEXT_PUBLIC_ is inlined into the client bundle and shipped to every visitor. Developers routinely prefix a secret to "fix" an undefined variable and silently publish it.
# Only PUBLIC values should appear here:
grep -r "NEXT_PUBLIC_" .env*
Rule: NEXT_PUBLIC_* is for non-secret config only (analytics IDs, public anon keys). A Stripe secret key, a service-role key, or a database URL must never carry that prefix. If one did, rotate it — it's already public.
2. Environment scoping
Vercel has three environments: Production, Preview, Development. Don't reuse production secrets in preview — preview URLs are easier to discover and share. Scope secrets per environment in the dashboard, and use separate API keys/databases for preview where you can.
3. Protect preview deployments
Every push spins up a public preview URL. If your previews touch real data or expose unfinished admin tooling, enable Deployment Protection (Vercel Authentication or password) so previews aren't crawlable by anyone with the URL.
4. Security headers via config
Set headers once in next.config.js so every route is covered:
// next.config.js
const securityHeaders = [
{ key: "Strict-Transport-Security", value: "max-age=63072000; includeSubDomains; preload" },
{ key: "X-Content-Type-Options", value: "nosniff" },
{ key: "X-Frame-Options", value: "DENY" },
{ key: "Referrer-Policy", value: "strict-origin-when-cross-origin" },
{ key: "Content-Security-Policy", value: "default-src 'self'" },
];
module.exports = {
async headers() {
return [{ source: "/:path*", headers: securityHeaders }];
},
};
Confirm they actually ship:
curl -sI https://yourapp.vercel.app | grep -i "strict-transport\|content-security"
5. Lock down Route Handlers and Server Actions
- Every
app/api/*/route.tsis public by default — add auth checks inside. - Server Actions in modern Next.js validate the
OriginagainstallowedOrigins, but you still must authorize the action (is this user allowed to do this?). - Don't return internal error details to the client.
6. Don't trust middleware as your only auth gate
Next.js middleware runs on the edge and is fine for redirects, but it has historically had bypass edge cases and doesn't see everything. Enforce authorization at the data layer too (in the route handler / Server Component that reads data), not only in middleware.
7. Cron and webhook endpoints need secrets
Vercel Cron and external webhooks hit public URLs. Require a shared secret:
export async function GET(req: Request) {
if (req.headers.get("authorization") !== `Bearer ${process.env.CRON_SECRET}`) {
return new Response("Unauthorized", { status: 401 });
}
// ... do the job
}
For Stripe and similar, verify the signature, not just a bearer token.
8. Source maps and verbose builds
Avoid shipping source maps that expose your server logic to the browser in production, and make sure your error pages don't print stack traces.
9. Custom domains and HSTS preload
Once you're on a stable custom domain over HTTPS, consider submitting it to the HSTS preload list so browsers force HTTPS from the first visit.
10. Least-privilege integrations
Vercel integrations (databases, analytics) often request broad scopes. Grant the minimum, and review which integrations have access to your project periodically.
Pre-deploy checklist
- No secrets behind
NEXT_PUBLIC_ - Secrets scoped per environment
- Preview deployments protected
- Security headers in
next.config.jsand verified live - Route handlers/actions enforce auth + authorization
- Auth enforced at the data layer, not just middleware
- Cron/webhook endpoints require a secret or signature
- No production source maps / leaked stack traces
Scan it with Troja
Troja checks your deployed Vercel app for the leaked NEXT_PUBLIC_ secret, missing headers, unprotected API routes, and exposed previews — then hands you a fix prompt for each. Point it at your production URL before your users (or attackers) find the gaps.
Run the scan this post is about.
Free, no signup. See what's hiding inside your walls in ~30 seconds.
Keep reading
All postsTroja vs. checkvibe: the closest scanner comparison (2026)
checkvibe pioneered security + SEO + AEO scanning with AI fix prompts and a 7-engine matrix. Troja matches it and adds connected deep-stack scans. The honest comparison.
ReadTroja vs. Fixnx: which AI website scanner should you use?
Fixnx runs 100+ AI-powered security, SEO and speed checks with credit-pack pricing. Troja adds AEO, connected deep-stack scans and per-finding AI fixes. Compared.
ReadTroja vs. CyScan.io: recon tool vs. fix-it scanner
CyScan.io is a free attack-surface recon scanner — endpoints, subdomains, fuzzing, screenshots. Troja is a fix-and-ship scanner with AI fixes, AEO and deep-stack scans.
Read