How to Secure Your Vercel App
Vercel provides excellent infrastructure security, but application-level security is your responsibility. Missing security headers, exposed secrets in preview deployments, and unprotected API routes are the most common issues. This guide covers everything you need to lock down your Vercel application.
Find security issues automatically before attackers do.
Follow These Steps
Configure security headers in next.config.js
Vercel does not add security headers by default. Add them through your Next.js configuration to protect against common browser-based attacks.
// next.config.js
const securityHeaders = [
{ key: 'X-Content-Type-Options', value: 'nosniff' },
{ key: 'X-Frame-Options', value: 'DENY' },
{ key: 'X-XSS-Protection', value: '0' },
{ key: 'Referrer-Policy', value: 'strict-origin-when-cross-origin' },
{ key: 'Strict-Transport-Security', value: 'max-age=63072000; includeSubDomains; preload' },
{ key: 'Permissions-Policy', value: 'camera=(), microphone=(), geolocation=()' }
]
module.exports = {
async headers() {
return [{ source: '/(.*)', headers: securityHeaders }]
}
}Protect environment variables across environments
Vercel has separate environment variable scopes: Production, Preview, and Development. Ensure secrets are only available in the environments that need them.
# In Vercel Dashboard > Settings > Environment Variables
# Set sensitive vars for Production only
# DATABASE_URL -> Production
# STRIPE_SECRET_KEY -> Production
# For preview deployments, use separate test keys
# STRIPE_SECRET_KEY -> Preview (use test key)
# Never use NEXT_PUBLIC_ for secrets
# NEXT_PUBLIC_* vars are embedded in the JS bundleEnable "Sensitive" flag on environment variables to hide their values in the Vercel dashboard and logs.
Secure preview deployments
Preview deployments are publicly accessible by default. Protect them with Vercel Authentication or password protection to prevent data leaks from staging environments.
// vercel.json - Protect preview deployments
{
"passwordProtection": {
"deploymentType": "preview"
}
}
// Or use Vercel Authentication (Team plan)
// Settings > General > Deployment Protection > Vercel AuthenticationAdd authentication to API routes
Every API route that reads or writes data must verify the user session. Use middleware to protect entire route groups.
// middleware.ts
import { auth } from '@/auth'
import { NextResponse } from 'next/server'
export default auth((req) => {
if (!req.auth && req.nextUrl.pathname.startsWith('/api/')) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
})
export const config = {
matcher: ['/api/:path*', '/dashboard/:path*']
}Implement Content Security Policy
Add a CSP header to prevent XSS attacks by controlling which resources the browser can load.
// middleware.ts
import { NextResponse } from 'next/server'
export function middleware(request: Request) {
const nonce = Buffer.from(crypto.randomUUID()).toString('base64')
const csp = [
`default-src 'self'`,
`script-src 'self' 'nonce-${nonce}'`,
`style-src 'self' 'unsafe-inline'`,
`img-src 'self' data: https:`,
`font-src 'self'`,
`connect-src 'self' https://api.yourdomain.com`,
`frame-ancestors 'none'`
].join('; ')
const response = NextResponse.next()
response.headers.set('Content-Security-Policy', csp)
return response
}Rate limit your API routes
Vercel Serverless Functions can be hit by anyone. Add rate limiting using Vercel KV or an external service.
import { Ratelimit } from '@upstash/ratelimit'
import { Redis } from '@upstash/redis'
const ratelimit = new Ratelimit({
redis: Redis.fromEnv(),
limiter: Ratelimit.slidingWindow(10, '10 s'),
})
export async function POST(req: Request) {
const ip = req.headers.get('x-forwarded-for') ?? '127.0.0.1'
const { success } = await ratelimit.limit(ip)
if (!success) {
return Response.json({ error: 'Too many requests' }, { status: 429 })
}
// Process request
}Scan your Vercel deployment
Run a VAS security scan against your production Vercel URL to verify all headers, authentication, and other security measures are properly configured.
Also scan preview deployment URLs to ensure they are properly protected.
What You'll Achieve
Your Vercel app now has comprehensive security headers, protected environment variables, secured preview deployments, authenticated API routes, Content Security Policy, and rate limiting. Your Vercel deployment follows security best practices.
Common Mistakes to Avoid
Mistake
Leaving preview deployments publicly accessible
Fix
Enable password protection or Vercel Authentication for preview deployments. They may contain staging data or features not ready for public access.
Mistake
Using the same API keys for preview and production
Fix
Use separate test/sandbox keys for preview environments. This prevents preview deployments from affecting production data or billing.
Mistake
Not rate limiting serverless functions
Fix
Without rate limiting, attackers can trigger thousands of function invocations, running up your bill. Use Upstash Ratelimit or similar.
Frequently Asked Questions
Does Vercel provide security headers automatically?
Vercel provides HTTPS and DDoS protection at the infrastructure level, but does not add application security headers like CSP, X-Frame-Options, or HSTS. You must configure these in your application.
Are Vercel preview deployments secure?
Preview deployments are publicly accessible by default. Anyone with the URL can access them. Enable deployment protection in Vercel settings to restrict access.
How do I protect environment variables in Vercel?
Use Vercel environment variable scopes (Production, Preview, Development) to control which secrets are available where. Mark sensitive values with the Sensitive flag. Never use NEXT_PUBLIC_ prefix for secrets.
Ready to Secure Your App?
VAS automatically scans your deployed app for the security issues covered in this guide. Get actionable results in minutes.
Start Security Scan