Vybex

Security Setup

One-click security config for your stack. Copy, paste, done.

Framework
Deploy To
Security Headers
Protects against XSS, clickjacking, MIME sniffing, and more.
Protects: XSS, Clickjacking, MIME Sniffing, Referrer Leaks
next.config.ts
const nextConfig = {
  async headers() {
    return [{
      source: '/(.*)',
      headers: [
        { key: 'X-Frame-Options', value: 'DENY' },
        { key: 'X-Content-Type-Options', value: 'nosniff' },
        { key: 'Referrer-Policy', value: 'strict-origin-when-cross-origin' },
        { key: 'X-XSS-Protection', value: '1; mode=block' },
        { key: 'Strict-Transport-Security', value: 'max-age=31536000; includeSubDomains' },
        { key: 'Permissions-Policy', value: 'camera=(), microphone=(), geolocation=()' },
        { key: 'Content-Security-Policy', value: "default-src 'self'; script-src 'self' 'unsafe-eval' 'unsafe-inline'; style-src 'self' 'unsafe-inline';" },
      ],
    }];
  },
  productionBrowserSourceMaps: false,
};

export default nextConfig;
CORS Configuration
Only allow requests from YOUR domain. Never use wildcard '*' in production.
Protects: Cross-Origin Attacks, Data Theft
src/middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';

const allowedOrigins = [
  'https://yourdomain.com',
  process.env.NODE_ENV === 'development' ? 'http://localhost:3000' : '',
].filter(Boolean);

export function middleware(request: NextRequest) {
  const origin = request.headers.get('origin') ?? '';
  const res = NextResponse.next();

  if (allowedOrigins.includes(origin)) {
    res.headers.set('Access-Control-Allow-Origin', origin);
    res.headers.set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
    res.headers.set('Access-Control-Allow-Headers', 'Content-Type, Authorization');
    res.headers.set('Access-Control-Allow-Credentials', 'true');
  }

  return res;
}
Rate Limiting
Prevents brute-force attacks and API abuse. Essential for login endpoints.
Protects: Brute Force, DDoS, API Abuse
src/lib/rate-limit.ts
import { NextResponse } from 'next/server';

const rateLimit = new Map<string, { count: number; resetTime: number }>();

export function rateLimitCheck(ip: string, limit = 10, windowMs = 60000) {
  const now = Date.now();
  const record = rateLimit.get(ip);

  if (!record || now > record.resetTime) {
    rateLimit.set(ip, { count: 1, resetTime: now + windowMs });
    return { allowed: true, remaining: limit - 1 };
  }

  if (record.count >= limit) {
    return { allowed: false, remaining: 0 };
  }

  record.count++;
  return { allowed: true, remaining: limit - record.count };
}

// Usage in API route:
// const { allowed } = rateLimitCheck(request.ip ?? '127.0.0.1', 5, 900000);
// if (!allowed) return NextResponse.json({ error: 'Too many requests' }, { status: 429 });
Environment Variable Validation
Crash early if required env vars are missing. Never run with missing secrets.
Protects: Misconfiguration, Secret Exposure
src/lib/env.ts
const requiredEnvVars = [
  'DATABASE_URL',
  'JWT_SECRET',
  'NEXT_PUBLIC_APP_URL',
] as const;

type EnvVars = Record<(typeof requiredEnvVars)[number], string>;

function validateEnv(): EnvVars {
  const missing: string[] = [];

  for (const key of requiredEnvVars) {
    if (!process.env[key]) {
      missing.push(key);
    }
  }

  if (missing.length > 0) {
    throw new Error(
      `Missing required environment variables:\n${missing.map(k => `  - ${k}`).join('\n')}`
    );
  }

  return Object.fromEntries(
    requiredEnvVars.map(key => [key, process.env[key]!])
  ) as EnvVars;
}

export const env = validateEnv();
Security .gitignore
Prevents accidentally committing secrets, keys, and sensitive files.
Protects: Secret Exposure, Source Code Leak
.gitignore
# Dependencies
node_modules/

# Environment
.env
.env.*
!.env.example

# Secrets & Keys
*.pem
*.key
*.cert
*.p12

# Build
.next/
dist/
build/
.vercel/

# IDE
.idea/
.vscode/settings.json

# OS
.DS_Store
Thumbs.db

# Debug
*.log
npm-debug.log*

# Database
*.sql
*.dump
*.sqlite