Vercel Security Best Practices: Headers, Env Vars, and Deployment

Share

TL;DR

The #1 Vercel security best practice is configuring security headers in vercel.json. These 7 practices take about 55 minutes to implement and address 81% of security issues found in Vercel deployments. Focus on: adding security headers, keeping secrets out of NEXT_PUBLIC_ variables, protecting preview deployments, and using Edge Middleware for authentication.

"Vercel secures the infrastructure; you secure the application. Headers, environment variables, and middleware are your responsibility."

Best Practice 1: Add Security Headers 5 min

Security headers protect against common web attacks. Configure them in vercel.json:

vercel.json with security headers
{
  "headers": [
    {
      "source": "/(.*)",
      "headers": [
        {
          "key": "X-Content-Type-Options",
          "value": "nosniff"
        },
        {
          "key": "X-Frame-Options",
          "value": "DENY"
        },
        {
          "key": "X-XSS-Protection",
          "value": "1; mode=block"
        },
        {
          "key": "Referrer-Policy",
          "value": "strict-origin-when-cross-origin"
        },
        {
          "key": "Permissions-Policy",
          "value": "camera=(), microphone=(), geolocation=()"
        }
      ]
    }
  ]
}

Content Security Policy

Add CSP for additional protection (customize based on your app's needs):

CSP header example
{
  "key": "Content-Security-Policy",
  "value": "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' https://fonts.gstatic.com; connect-src 'self' https://api.yourdomain.com"
}

Best Practice 2: Secure Environment Variables 3 min

Vercel has specific rules for environment variable exposure:

PrefixExposed to Browser?Use For
NEXT_PUBLIC_YesPublic API URLs, feature flags
No prefixNo (server only)API keys, secrets, credentials

Important: Any environment variable starting with NEXT_PUBLIC_ will be included in your client-side bundle and visible to users. Never put secrets in NEXT_PUBLIC_ variables.

Correct environment variable usage
// Safe: Server-side only (no NEXT_PUBLIC_ prefix)
const stripeSecretKey = process.env.STRIPE_SECRET_KEY;
const databaseUrl = process.env.DATABASE_URL;

// Safe: Intentionally public
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL;
const appUrl = process.env.NEXT_PUBLIC_APP_URL;

// WRONG: Secret with public prefix
// const apiSecret = process.env.NEXT_PUBLIC_API_SECRET; // Never do this!

Best Practice 3: Protect Preview Deployments 2 min

Preview deployments can expose unfinished features or test data. Protect them:

Vercel Authentication

  1. Go to Project Settings in Vercel Dashboard
  2. Navigate to Deployment Protection
  3. Enable Vercel Authentication for Preview deployments
  4. Configure which team members can access previews

Password Protection

For additional protection, add password authentication to previews:

Deployment Protection settings
// In Vercel Dashboard > Settings > Deployment Protection
{
  "protection": {
    "preview": {
      "enabled": true,
      "password": true  // Requires password for access
    }
  }
}

Best Practice 4: Use Edge Middleware for Auth 15 min

Protect routes at the edge before they reach your application:

middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';

export function middleware(request: NextRequest) {
  // Check for auth token
  const token = request.cookies.get('auth-token');

  // Protected routes
  if (request.nextUrl.pathname.startsWith('/dashboard')) {
    if (!token) {
      return NextResponse.redirect(new URL('/login', request.url));
    }
  }

  // Admin routes
  if (request.nextUrl.pathname.startsWith('/admin')) {
    if (!token) {
      return NextResponse.redirect(new URL('/login', request.url));
    }
    // Additional admin check would go here
  }

  return NextResponse.next();
}

export const config = {
  matcher: ['/dashboard/:path*', '/admin/:path*', '/api/protected/:path*']
};

Best Practice 5: Secure API Routes 15 min

Vercel API routes (or Next.js route handlers) need proper security:

Secure API route pattern
// app/api/user/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { verifyAuth } from '@/lib/auth';

export async function GET(request: NextRequest) {
  // Verify authentication
  const authResult = await verifyAuth(request);
  if (!authResult.success) {
    return NextResponse.json(
      { error: 'Unauthorized' },
      { status: 401 }
    );
  }

  // Rate limiting check
  const rateLimitResult = await checkRateLimit(authResult.userId);
  if (!rateLimitResult.allowed) {
    return NextResponse.json(
      { error: 'Too many requests' },
      { status: 429 }
    );
  }

  // Proceed with authenticated request
  const userData = await getUserData(authResult.userId);
  return NextResponse.json(userData);
}

export async function POST(request: NextRequest) {
  const authResult = await verifyAuth(request);
  if (!authResult.success) {
    return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
  }

  // Validate input
  const body = await request.json();
  const validation = validateUserUpdate(body);
  if (!validation.success) {
    return NextResponse.json(
      { error: 'Invalid input', details: validation.errors },
      { status: 400 }
    );
  }

  // Process request...
}

Best Practice 6: Configure CORS Properly 10 min

For API routes that need CORS, configure it explicitly:

CORS configuration in API route
// app/api/public/route.ts
import { NextRequest, NextResponse } from 'next/server';

const allowedOrigins = [
  'https://yourdomain.com',
  'https://app.yourdomain.com',
];

export async function OPTIONS(request: NextRequest) {
  const origin = request.headers.get('origin');

  if (origin && allowedOrigins.includes(origin)) {
    return new NextResponse(null, {
      status: 204,
      headers: {
        'Access-Control-Allow-Origin': origin,
        'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
        'Access-Control-Allow-Headers': 'Content-Type, Authorization',
        'Access-Control-Max-Age': '86400',
      },
    });
  }

  return new NextResponse(null, { status: 403 });
}

export async function GET(request: NextRequest) {
  const origin = request.headers.get('origin');
  const response = NextResponse.json({ data: 'example' });

  if (origin && allowedOrigins.includes(origin)) {
    response.headers.set('Access-Control-Allow-Origin', origin);
  }

  return response;
}

Best Practice 7: Monitor and Alert 5 min

Set up monitoring for your Vercel deployment:

  • Vercel Analytics: Monitor performance and errors
  • Log Drains: Send logs to external monitoring services
  • Status Alerts: Get notified of deployment issues
  • Speed Insights: Track Core Web Vitals

Vercel Security Checklist:

  • Security headers configured in vercel.json
  • No secrets in NEXT_PUBLIC_ environment variables
  • Preview deployments protected with authentication
  • Edge Middleware protecting sensitive routes
  • API routes validate authentication and input
  • CORS configured with specific allowed origins
  • Monitoring and alerts enabled

Official Resources: For the latest information, see Vercel Security Documentation, Vercel Headers Configuration, and Deployment Protection Guide.

Is Vercel secure by default?

Vercel provides a secure infrastructure with automatic HTTPS, DDoS protection, and isolated builds. However, application-level security like headers, environment variable handling, and authentication is your responsibility to configure.

How do I protect API routes on Vercel?

Use Edge Middleware for route-level protection, implement authentication checks in each API route, add rate limiting, and validate all input. Never trust client-side data without server-side validation.

Are environment variables safe on Vercel?

Server-side environment variables (without NEXT_PUBLIC_ prefix) are encrypted and only available during build and runtime on the server. Variables with NEXT_PUBLIC_ prefix are bundled into client code and visible to users.

Should I protect preview deployments?

Yes. Preview deployments can expose unfinished features, test data, or security vulnerabilities. Enable Vercel Authentication or password protection for all preview deployments, especially for production projects.

Verify Your Vercel Security

Scan your Vercel deployment for security headers and configuration issues.

Start Free Scan
Best Practices

Vercel Security Best Practices: Headers, Env Vars, and Deployment