Security Overview

Defense-in-depth security architecture with middleware chain, rate limiting, input validation, and security headers

Kit ships with a defense-in-depth security architecture that protects your application at multiple layers. Every API request passes through rate limiting, input validation, sanitization, security headers, and CORS — all pre-configured and production-ready out of the box.
This page provides the architectural overview. For implementation details, see Rate Limiting & Validation and Headers & CORS.

Defense-in-Depth Architecture

Security is implemented in five concentric layers. An attack must bypass all layers to succeed — if any single layer catches it, the request is blocked:
┌─────────────────────────────────────────────────────────────┐
│  Layer 1: Middleware                                        │
│  CORS preflight, Clerk authentication, security headers     │
│                                                             │
│  ┌─────────────────────────────────────────────────────┐    │
│  │  Layer 2: Rate Limiting                             │    │
│  │  Category-based limits (upload, email, payments)    │    │
│  │                                                     │    │
│  │  ┌─────────────────────────────────────────────┐    │    │
│  │  │  Layer 3: Input Validation                  │    │    │
│  │  │  Zod schemas for every API endpoint         │    │    │
│  │  │                                             │    │    │
│  │  │  ┌─────────────────────────────────────┐    │    │    │
│  │  │  │  Layer 4: Sanitization              │    │    │    │
│  │  │  │  XSS, SQL, path traversal, URLs     │    │    │    │
│  │  │  │                                     │    │    │    │
│  │  │  │  ┌─────────────────────────────┐    │    │    │    │
│  │  │  │  │  Layer 5: Error Handling    │    │    │    │    │
│  │  │  │  │  Safe responses, no leaks   │    │    │    │    │
│  │  │  │  └─────────────────────────────┘    │    │    │    │
│  │  │  └─────────────────────────────────────┘    │    │    │
│  │  └─────────────────────────────────────────────┘    │    │
│  └─────────────────────────────────────────────────────┘    │
└─────────────────────────────────────────────────────────────┘
Each layer operates independently — they do not depend on each other to function. This means disabling rate limiting (for example, in local development without Redis) does not weaken input validation or security headers.

Middleware Chain

Every request that reaches your application passes through the middleware chain defined in middleware.ts. The chain executes in a specific order, with each step able to short-circuit the request:
Incoming Request
    |
    v
1. Test Environment Check
    |--- Test/CI? ──> NextResponse.next() (bypass all security)
    |
    v
2. Blog Feature Flag
    |--- Blog disabled + /blog route? ──> Redirect to /
    |
    v
3. CORS Preflight
    |--- OPTIONS request? ──> Return 204 with CORS headers (stop here)
    |
    v
4. Clerk Authentication
    |--- Public route? ──> Continue (auth state available but not required)
    |--- Protected route? ──> auth.protect() (redirect to /login if unauthenticated)
    |
    v
5. Security Headers
    |--- Apply HSTS, X-Frame-Options, CSP, Permissions-Policy, etc.
    |
    v
6. CORS Response Headers
    |--- Add Access-Control-Allow-Origin and related headers
    |
    v
Response
The middleware is designed for fail-safe operation. If any step throws an error, the catch block returns NextResponse.next() to avoid blocking legitimate requests:
src/middleware.ts — Fail-safe middleware entry
// Main middleware function with security enhancements
async function middleware(request: NextRequest) {
  // Always bypass in test/CI environments
  if (isTestEnvironment()) {
    return NextResponse.next()
  }

Public vs Protected Routes

The middleware distinguishes between public routes (accessible to anyone) and protected routes (requiring authentication). Public routes are defined as an explicit allowlist:
src/middleware.ts — Public routes configuration
// Define public routes that don't require authentication
      const isPublicRoute = createRouteMatcher([
        '/login(.*)',
        '/register(.*)',
        '/privacy(.*)',
        '/terms(.*)',
        '/imprint(.*)',
        '/payment/(.*)',
        '/email-preview(.*)',
        '/logout(.*)',
        '/api/health(.*)',
        '/api/pricing(.*)',
        '/api/webhooks/lemonsqueezy(.*)',
        '/api/webhooks/resend(.*)',
        '/api/webhooks/clerk(.*)',
        '/robots.txt',
        '/sitemap.xml',
      ])

Route Categories

CategoryRoutesAuth RequiredNotes
Marketing/, /about, /blog, /pricing, /contactNoPublic-facing pages
Auth/login, /register, /logoutNoClerk handles auth flow
Public API/api/health, /api/pricing, /api/contact, /api/newsletterNoOpen endpoints
Webhooks/api/webhooks/lemonsqueezy, /api/webhooks/resend, /api/webhooks/clerkNoSignature-verified externally
SEO/robots.txt, /sitemap.xmlNoSearch engine files
Dashboard/dashboard/**YesAll dashboard routes
Protected API/api/** (not listed above)YesAll other API endpoints
Any route not in the public list requires a valid Clerk session. Unauthenticated users are redirected to /login.

Fail-Open Philosophy

Kit's security system is designed to degrade gracefully rather than fail catastrophically. This is a deliberate architectural decision:
ScenarioBehaviorRationale
Redis unavailableRate limiting disabled, requests allowedDon't block users due to infrastructure issues
Clerk fails to loadMiddleware bypassedDon't break the app if auth service is down
Security headers import failsRequest continues without headersHeaders are a defense layer, not a gatekeeper
Rate limit check throwsRequest allowed throughFail-open is safer than fail-closed for SaaS
Error in any security layer?
    |
    ├── Log warning (for monitoring)
    |
    └── Allow request to continue
        (other layers still active)
The rationale: for a SaaS application, blocking legitimate paying customers is a worse outcome than temporarily allowing a few extra requests. Each layer logs warnings when it degrades, so you can detect and fix the root cause.

Safe Error Handling

The fifth and innermost layer ensures that error responses never leak sensitive information. Kit includes sanitizeErrorMessage() which strips stack traces, database connection strings, and internal details from API responses:
Development (NODE_ENV=development):
    Error: "Connection refused at postgres://user:pass@host:5432"
    → Returned as-is (helpful for debugging)

Production (NODE_ENV=production):
    Error: "Connection refused at postgres://user:pass@host:5432"
    → Returned as: "An unexpected error occurred"

Known safe errors (any environment):
    Error: "Validation failed: email is required"
    → Returned as-is (safe for users to see)
Errors starting with known safe prefixes (Validation failed, Invalid, Required, Not found, Unauthorized, Forbidden) pass through unchanged. All other errors are replaced with a generic message in production.

OWASP Coverage

Kit's security layers address the most common web application vulnerabilities:
OWASP CategoryKit ProtectionLayer
Injection (SQL, XSS, Command)Zod validation + sanitization functions3 & 4
Broken AuthenticationClerk middleware with route protection1
Sensitive Data ExposureSecurity headers (HSTS, no-sniff) + safe error messages1 & 5
Broken Access ControlPublic/protected route separation + Clerk auth.protect()1
Security MisconfigurationPre-configured security headers + CORS1
Cross-Site Scripting (XSS)sanitizeHtml() + X-XSS-Protection header1 & 4
Rate Limiting AbuseCategory-based rate limiting with Upstash Redis2

Key Files

FilePurposeLines
apps/boilerplate/src/middleware.tsMiddleware chain — CORS, Clerk auth, security headers172
apps/boilerplate/src/lib/security/api-rate-limiter.tsCategory-based rate limiting with Upstash Redis349
apps/boilerplate/src/lib/security/rate-limit-middleware.tswithRateLimit() middleware factory for API routes231
apps/boilerplate/src/lib/security/sanitization.tsXSS, filename, URL, email, SQL, JSON sanitization342
apps/boilerplate/src/lib/security/security-headers.tsSecurity headers generation (HSTS, CSP, CORP, etc.)200
apps/boilerplate/src/lib/security/cors-middleware.tsCORS configuration and middleware216
apps/boilerplate/src/lib/validations/api-schemas.tsCentralized Zod schemas for all API endpoints208