npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

nextjs-secure

v0.7.0

Published

Production-ready security middleware for Next.js 13+ App Router

Downloads

791

Readme

nextjs-secure

npm version License: MIT TypeScript Next.js Tests

Production-ready security middleware for Next.js 13+ App Router. Zero config, maximum protection.

import { withRateLimit, withJWT, withValidation } from 'nextjs-secure'

export const POST = withRateLimit(
  withJWT(
    withValidation(handler, { body: schema }),
    { secret: process.env.JWT_SECRET }
  ),
  { limit: 100, window: '15m' }
)

Why nextjs-secure?

Building secure APIs in Next.js shouldn't require hours of boilerplate. Most projects end up with copy-pasted rate limiting code, inconsistent error handling, and security gaps.

nextjs-secure provides battle-tested security primitives that work out of the box:

  • Zero Configuration - Sensible defaults, works immediately
  • Type Safe - Full TypeScript support with generics
  • Edge Ready - Works on Vercel Edge, Cloudflare Workers, Node.js
  • Flexible - Memory, Redis, or Upstash storage backends
  • Lightweight - No bloated dependencies, tree-shakeable
  • Complete - Rate limiting, auth, CSRF, headers, validation, audit logging

Installation

npm install nextjs-secure
# or
yarn add nextjs-secure
# or
pnpm add nextjs-secure

Table of Contents


Quick Start

Protect an API Route

// app/api/posts/route.ts
import { withRateLimit } from 'nextjs-secure'

export const GET = withRateLimit(
  async (request) => {
    const posts = await db.posts.findMany()
    return Response.json(posts)
  },
  { limit: 100, window: '15m' }
)

Full Security Stack

// app/api/admin/users/route.ts
import { withRateLimit, withJWT, withRoles, withValidation, withAuditLog } from 'nextjs-secure'
import { MemoryStore } from 'nextjs-secure/audit'

const auditStore = new MemoryStore({ maxEntries: 1000 })

const schema = {
  email: { type: 'email', required: true },
  role: { type: 'string', enum: ['user', 'admin'] }
}

export const POST = withAuditLog(
  withRateLimit(
    withJWT(
      withRoles(
        withValidation(
          async (req, ctx) => {
            // ctx.user = authenticated user
            // ctx.validated = validated body
            return Response.json({ success: true })
          },
          { body: schema }
        ),
        { roles: ['admin'] }
      ),
      { secret: process.env.JWT_SECRET }
    ),
    { limit: 10, window: '1m' }
  ),
  { store: auditStore }
)

Rate Limiting

Protect your APIs from abuse with configurable rate limiting.

Basic Usage

import { withRateLimit } from 'nextjs-secure'

export const GET = withRateLimit(handler, {
  limit: 100,        // Max requests allowed
  window: '15m',     // Time window
})

Window formats:

  • Seconds: '30s', '120s'
  • Minutes: '15m', '60m'
  • Hours: '1h', '24h'
  • Days: '1d', '7d'
  • Combined: '1h 30m'
  • Milliseconds: 900000

Algorithms

Sliding Window (Default)

Prevents request bursts at window boundaries.

export const GET = withRateLimit(handler, {
  limit: 100,
  window: '15m',
  algorithm: 'sliding-window',
})

Fixed Window

Simple counter that resets at fixed intervals.

export const GET = withRateLimit(handler, {
  limit: 100,
  window: '1m',
  algorithm: 'fixed-window',
})

Token Bucket

Allows controlled bursts while maintaining average rate.

export const GET = withRateLimit(handler, {
  limit: 100,
  window: '1m',
  algorithm: 'token-bucket',
})

Storage Backends

Memory Store (Default)

import { withRateLimit, MemoryStore } from 'nextjs-secure'

const store = new MemoryStore({
  cleanupInterval: 60000,
  maxKeys: 10000,
})

export const GET = withRateLimit(handler, { limit: 100, window: '15m', store })

Redis Store

import Redis from 'ioredis'
import { withRateLimit, createRedisStore } from 'nextjs-secure/rate-limit'

const redis = new Redis(process.env.REDIS_URL)
const store = createRedisStore({ client: redis, prefix: 'myapp:rl' })

export const GET = withRateLimit(handler, { limit: 100, window: '15m', store })

Upstash Store (Edge/Serverless)

import { withRateLimit, createUpstashStore } from 'nextjs-secure/rate-limit'

const store = createUpstashStore({
  url: process.env.UPSTASH_REDIS_REST_URL,
  token: process.env.UPSTASH_REDIS_REST_TOKEN,
})

export const GET = withRateLimit(handler, { limit: 100, window: '15m', store })

Custom Identifiers

// By API Key
export const GET = withRateLimit(handler, {
  limit: 1000,
  window: '1h',
  identifier: (req) => req.headers.get('x-api-key') ?? 'anonymous',
})

// By User ID
export const GET = withRateLimit(handler, {
  limit: 100,
  window: '15m',
  identifier: async (req) => {
    const session = await getSession(req)
    return session?.userId ?? getClientIp(req)
  },
})

Response Headers

| Header | Description | |--------|-------------| | X-RateLimit-Limit | Maximum requests allowed | | X-RateLimit-Remaining | Requests remaining | | X-RateLimit-Reset | Unix timestamp when limit resets | | Retry-After | Seconds until retry (only on 429) |


CSRF Protection

Protect forms against Cross-Site Request Forgery attacks.

Basic Setup

// app/api/csrf/route.ts - Token endpoint
import { generateCSRF } from 'nextjs-secure/csrf'

export async function GET() {
  const { token, cookieHeader } = await generateCSRF()
  return Response.json(
    { csrfToken: token },
    { headers: { 'Set-Cookie': cookieHeader } }
  )
}
// app/api/submit/route.ts - Protected endpoint
import { withCSRF } from 'nextjs-secure/csrf'

export const POST = withCSRF(async (req) => {
  const data = await req.json()
  return Response.json({ success: true })
})

Client-Side Usage

// Fetch token on page load
const { csrfToken } = await fetch('/api/csrf').then(r => r.json())

// Include in form submissions
fetch('/api/submit', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'x-csrf-token': csrfToken
  },
  body: JSON.stringify({ data: '...' })
})

Configuration

export const POST = withCSRF(handler, {
  cookie: {
    name: '__csrf',
    httpOnly: true,
    secure: true,
    sameSite: 'strict',
    maxAge: 86400
  },
  headerName: 'x-csrf-token',
  fieldName: '_csrf',
  secret: process.env.CSRF_SECRET,
  skip: (req) => req.headers.get('x-api-key') === 'trusted',
})

Security Headers

Add security headers to protect against common attacks.

Quick Start

import { withSecurityHeaders } from 'nextjs-secure/headers'

export const GET = withSecurityHeaders(handler)

Presets

// Strict: Maximum security (default)
export const GET = withSecurityHeaders(handler, { preset: 'strict' })

// Relaxed: Development-friendly
export const GET = withSecurityHeaders(handler, { preset: 'relaxed' })

// API: Optimized for JSON APIs
export const GET = withSecurityHeaders(handler, { preset: 'api' })

Custom Configuration

export const GET = withSecurityHeaders(handler, {
  config: {
    contentSecurityPolicy: {
      defaultSrc: ["'self'"],
      scriptSrc: ["'self'", "'unsafe-inline'"],
      styleSrc: ["'self'", "'unsafe-inline'"],
      imgSrc: ["'self'", 'data:', 'https:'],
    },
    strictTransportSecurity: {
      maxAge: 31536000,
      includeSubDomains: true,
      preload: true,
    },
    xFrameOptions: 'DENY',
    xContentTypeOptions: true,
    referrerPolicy: 'strict-origin-when-cross-origin',
    permissionsPolicy: {
      camera: [],
      microphone: [],
      geolocation: [],
    },
  }
})

Available Headers

| Header | Description | |--------|-------------| | Content-Security-Policy | Controls resources the page can load | | Strict-Transport-Security | Forces HTTPS connections | | X-Frame-Options | Prevents clickjacking | | X-Content-Type-Options | Prevents MIME sniffing | | Referrer-Policy | Controls referrer information | | Permissions-Policy | Disables browser features | | Cross-Origin-Opener-Policy | Isolates browsing context | | Cross-Origin-Embedder-Policy | Controls embedding | | Cross-Origin-Resource-Policy | Controls resource sharing |


Authentication

Flexible authentication supporting JWT, API keys, sessions, and RBAC.

JWT Authentication

import { withJWT } from 'nextjs-secure/auth'

export const GET = withJWT(
  async (req, ctx) => {
    return Response.json({ user: ctx.user })
  },
  {
    secret: process.env.JWT_SECRET,
    algorithms: ['HS256', 'RS256'],
    issuer: 'https://myapp.com',
    audience: 'my-api',
  }
)

API Key Authentication

import { withAPIKey } from 'nextjs-secure/auth'

export const GET = withAPIKey(
  async (req, ctx) => {
    return Response.json({ user: ctx.user })
  },
  {
    validate: async (apiKey) => {
      const user = await db.users.findByApiKey(apiKey)
      return user || null
    },
    headerName: 'x-api-key',
    queryParam: 'api_key',
  }
)

Session Authentication

import { withSession } from 'nextjs-secure/auth'

export const GET = withSession(
  async (req, ctx) => {
    return Response.json({ user: ctx.user })
  },
  {
    validate: async (sessionId) => {
      const session = await db.sessions.find(sessionId)
      return session?.user || null
    },
    cookieName: 'session',
  }
)

Role-Based Access Control

import { withJWT, withRoles } from 'nextjs-secure/auth'

export const GET = withJWT(
  withRoles(
    async (req, ctx) => {
      return Response.json({ admin: true })
    },
    {
      roles: ['admin', 'moderator'],
      permissions: ['users:read', 'users:write'],
    }
  ),
  { secret: process.env.JWT_SECRET }
)

Combined Authentication

import { withAuth } from 'nextjs-secure/auth'

export const GET = withAuth(
  async (req, ctx) => {
    return Response.json({ user: ctx.user })
  },
  {
    jwt: { secret: process.env.JWT_SECRET },
    apiKey: { validate: (key) => db.apiKeys.findUser(key) },
    session: { validate: (id) => db.sessions.findUser(id) },
    rbac: { roles: ['user', 'admin'] },
  }
)

Optional Authentication

import { withOptionalAuth } from 'nextjs-secure/auth'

export const GET = withOptionalAuth(
  async (req, ctx) => {
    if (ctx.user) {
      return Response.json({ user: ctx.user })
    }
    return Response.json({ guest: true })
  },
  { jwt: { secret: process.env.JWT_SECRET } }
)

Input Validation

Validate and sanitize user input to prevent attacks.

Schema Validation

import { withValidation } from 'nextjs-secure/validation'

// Built-in schema
const schema = {
  email: { type: 'email', required: true },
  password: { type: 'string', minLength: 8, maxLength: 100 },
  age: { type: 'number', min: 18, max: 120 },
  role: { type: 'string', enum: ['user', 'admin'] },
}

export const POST = withValidation(handler, { body: schema })

// Or use Zod
import { z } from 'zod'

const zodSchema = z.object({
  email: z.string().email(),
  password: z.string().min(8),
})

export const POST = withValidation(handler, { body: zodSchema })

XSS Protection

import { withXSSProtection, withSanitization, sanitize, detectXSS } from 'nextjs-secure/validation'

// Block XSS attempts
export const POST = withXSSProtection(handler)

// Sanitize specific fields
export const POST = withSanitization(handler, {
  fields: ['content', 'bio'],
  mode: 'escape', // 'escape' | 'strip' | 'allow-safe'
})

// Manual sanitization
const clean = sanitize(userInput, {
  mode: 'allow-safe',
  allowedTags: ['b', 'i', 'em', 'strong'],
})

// Detection only
const { hasXSS, matches } = detectXSS(input)

SQL Injection Protection

import { withSQLProtection, detectSQLInjection, hasSQLInjection } from 'nextjs-secure/validation'

// Block SQL injection
export const POST = withSQLProtection(handler, {
  mode: 'block', // 'block' | 'detect'
  minSeverity: 'medium', // 'low' | 'medium' | 'high'
})

// Manual detection
const result = detectSQLInjection(input)
// { hasSQLi: true, severity: 'high', patterns: ['UNION SELECT'] }

// Simple check
if (hasSQLInjection(input)) {
  // Block request
}

Path Traversal Prevention

import { validatePath, sanitizePath, sanitizeFilename } from 'nextjs-secure/validation'

// Validate path
const result = validatePath(userPath, {
  basePath: '/uploads',
  allowedExtensions: ['.jpg', '.png'],
  maxDepth: 3,
})

if (!result.valid) {
  console.log(result.reason) // 'traversal_detected', 'invalid_extension', etc.
}

// Sanitize
const safePath = sanitizePath('../../../etc/passwd') // 'etc/passwd'
const safeFilename = sanitizeFilename('../../evil.exe') // 'evil.exe'

File Validation

import { withFileValidation, validateFile } from 'nextjs-secure/validation'

export const POST = withFileValidation(handler, {
  maxSize: 5 * 1024 * 1024, // 5MB
  allowedTypes: ['image/jpeg', 'image/png', 'application/pdf'],
  validateMagicNumbers: true, // Check actual file content
  maxFiles: 10,
})

// Manual validation
const result = await validateFile(file, {
  maxSize: 5 * 1024 * 1024,
  allowedTypes: ['image/jpeg'],
})

Combined Security Validation

import { withSecureValidation } from 'nextjs-secure/validation'

export const POST = withSecureValidation(handler, {
  xss: true,
  sql: { minSeverity: 'medium' },
  contentType: ['application/json'],
})

Audit Logging

Track requests and security events for monitoring and compliance.

Request Logging

import { withAuditLog, MemoryStore, ConsoleStore } from 'nextjs-secure/audit'

const store = new MemoryStore({ maxEntries: 1000 })

export const POST = withAuditLog(handler, {
  store,
  include: {
    ip: true,
    userAgent: true,
    headers: false,
    query: true,
    response: true,
    duration: true,
  },
  exclude: {
    paths: ['/health', '/metrics'],
    methods: ['OPTIONS'],
    statusCodes: [304],
  },
  pii: {
    fields: ['password', 'token', 'ssn', 'creditCard'],
    mode: 'mask', // 'mask' | 'hash' | 'remove'
  },
})

Storage Backends

import { MemoryStore, ConsoleStore, createDatadogStore, MultiStore } from 'nextjs-secure/audit'

// Memory (development)
const memoryStore = new MemoryStore({ maxEntries: 1000, ttl: 3600000 })

// Console (development)
const consoleStore = new ConsoleStore({ colorize: true, level: 'info' })

// Datadog (production)
const datadogStore = createDatadogStore({
  apiKey: process.env.DATADOG_API_KEY,
  service: 'my-api',
  environment: 'production',
})

// Multiple stores
const multiStore = new MultiStore([consoleStore, datadogStore])

Security Event Tracking

import { createSecurityTracker, trackSecurityEvent } from 'nextjs-secure/audit'

const tracker = createSecurityTracker({ store })

// Authentication failures
await tracker.authFailed({
  ip: '192.168.1.1',
  email: '[email protected]',
  reason: 'Invalid password',
})

// Rate limit exceeded
await tracker.rateLimitExceeded({
  ip: '192.168.1.1',
  endpoint: '/api/login',
  limit: 10,
  window: '15m',
})

// XSS detected
await tracker.xssDetected({
  ip: '192.168.1.1',
  field: 'comment',
  endpoint: '/api/comments',
})

// SQL injection detected
await tracker.sqliDetected({
  ip: '192.168.1.1',
  field: 'username',
  pattern: 'UNION SELECT',
  severity: 'high',
  endpoint: '/api/users',
})

// CSRF validation failure
await tracker.csrfInvalid({
  ip: '192.168.1.1',
  endpoint: '/api/transfer',
  reason: 'Token mismatch',
})

// IP blocked
await tracker.ipBlocked({
  ip: '192.168.1.1',
  reason: 'Too many failed attempts',
  duration: 3600,
})

// Custom events
await tracker.custom({
  message: 'Suspicious activity detected',
  severity: 'high',
  details: { pattern: 'automated_scanning' },
})

PII Redaction

import { redactObject, redactEmail, redactCreditCard, redactIP, DEFAULT_PII_FIELDS } from 'nextjs-secure/audit'

// Redact object
const safeData = redactObject(userData, {
  fields: DEFAULT_PII_FIELDS,
  mode: 'mask',
})

// Specific redactors
redactEmail('[email protected]')     // '****@example.com'
redactCreditCard('4111111111111111') // '**** **** **** 1111'
redactIP('192.168.1.100')           // '192.168.*.*'

Request ID & Timing

import { withRequestId, withTiming } from 'nextjs-secure/audit'

// Add request ID to responses
export const GET = withRequestId(handler, {
  headerName: 'x-request-id',
  generateId: () => `req_${Date.now()}`,
})

// Add response timing
export const GET = withTiming(handler, {
  headerName: 'x-response-time',
  log: true,
})

Log Formatters

import { JSONFormatter, TextFormatter, CLFFormatter, StructuredFormatter } from 'nextjs-secure/audit'

// JSON (default)
const jsonFormatter = new JSONFormatter({ pretty: true })

// Human-readable text
const textFormatter = new TextFormatter({
  template: '{timestamp} [{level}] {message}',
})

// Apache/Nginx Common Log Format
const clfFormatter = new CLFFormatter()

// Key=value (ELK/Splunk)
const structuredFormatter = new StructuredFormatter({
  delimiter: ' ',
  kvSeparator: '=',
})

Bot Detection

Protect your endpoints from automated bots, scrapers, and spam.

Basic Usage

import { withBotProtection } from 'nextjs-secure/bot'

export const POST = withBotProtection(handler, {
  userAgent: {
    blockAllBots: false,
    allowList: ['Googlebot', 'Bingbot'],
  },
  honeypot: true,
  behavior: {
    maxRequestsPerSecond: 10,
  },
})

Presets

import { withBotProtectionPreset } from 'nextjs-secure/bot'

// Relaxed: Only blocks obvious bots
export const GET = withBotProtectionPreset(handler, 'relaxed')

// Standard: Good balance (default)
export const GET = withBotProtectionPreset(handler, 'standard')

// Strict: Maximum protection
export const GET = withBotProtectionPreset(handler, 'strict')

// API: Optimized for API endpoints
export const GET = withBotProtectionPreset(handler, 'api')

User-Agent Detection

import { withUserAgentProtection, analyzeUserAgent, KNOWN_BOT_PATTERNS } from 'nextjs-secure/bot'

// Middleware
export const GET = withUserAgentProtection(handler, {
  blockAllBots: true,
  allowCategories: ['search_engine', 'social_media'],
  allowList: ['Googlebot', 'Twitterbot'],
  blockList: ['BadBot'],
})

// Manual detection
const result = analyzeUserAgent('Googlebot/2.1')
// { isBot: true, category: 'search_engine', name: 'Googlebot', confidence: 0.95 }

Honeypot Protection

import { withHoneypotProtection, generateHoneypotHTML, generateHoneypotCSS } from 'nextjs-secure/bot'

// Middleware
export const POST = withHoneypotProtection(handler, {
  fieldName: '_hp_email',
  additionalFields: ['_hp_name', '_hp_phone'],
})

// Generate HTML for forms
const honeypotHTML = generateHoneypotHTML({ fieldName: '_hp_email' })
// Returns hidden input fields

// Generate CSS
const honeypotCSS = generateHoneypotCSS({ fieldName: '_hp_email' })
// Returns CSS to hide fields

Behavior Analysis

import { withBehaviorProtection, MemoryBehaviorStore } from 'nextjs-secure/bot'

const store = new MemoryBehaviorStore()

export const GET = withBehaviorProtection(handler, {
  store,
  minRequestInterval: 100,    // Min ms between requests
  maxRequestsPerSecond: 10,   // Max requests per second
  patterns: {
    sequentialAccess: true,   // Detect sequential URL patterns
    regularTiming: true,      // Detect bot-like timing
    missingHeaders: true,     // Detect missing browser headers
  },
})

CAPTCHA Integration

import { withCaptchaProtection, verifyCaptcha } from 'nextjs-secure/bot'

// reCAPTCHA v3
export const POST = withCaptchaProtection(handler, {
  provider: 'recaptcha-v3',
  siteKey: process.env.RECAPTCHA_SITE_KEY,
  secretKey: process.env.RECAPTCHA_SECRET_KEY,
  threshold: 0.5,
})

// hCaptcha
export const POST = withCaptchaProtection(handler, {
  provider: 'hcaptcha',
  siteKey: process.env.HCAPTCHA_SITE_KEY,
  secretKey: process.env.HCAPTCHA_SECRET_KEY,
})

// Cloudflare Turnstile
export const POST = withCaptchaProtection(handler, {
  provider: 'turnstile',
  siteKey: process.env.TURNSTILE_SITE_KEY,
  secretKey: process.env.TURNSTILE_SECRET_KEY,
})

// Manual verification
const result = await verifyCaptcha(token, {
  provider: 'recaptcha-v3',
  secretKey: process.env.RECAPTCHA_SECRET_KEY,
})

Manual Bot Detection

import { detectBot } from 'nextjs-secure/bot'

const result = await detectBot(request, {
  userAgent: { blockAllBots: true },
  honeypot: true,
  behavior: { maxRequestsPerSecond: 10 },
})

if (result.isBot) {
  console.log(`Bot detected: ${result.reason}`)
  console.log(`Category: ${result.category}`)
  console.log(`Confidence: ${result.confidence}`)
}

Bot Categories

| Category | Examples | |----------|----------| | search_engine | Googlebot, Bingbot, Yandex | | social_media | Twitterbot, FacebookBot, LinkedInBot | | ai_crawler | GPTBot, Claude-Web, Anthropic | | monitoring | UptimeRobot, Pingdom | | feed_reader | Feedly, Feedbin | | preview | Slackbot, Discord | | scraper | Scrapy, DataMiner | | spam | Spam bots, malicious crawlers | | unknown | Unidentified automated traffic |


Utilities

Duration Parsing

import { parseDuration, formatDuration } from 'nextjs-secure'

parseDuration('15m')        // 900000
parseDuration('1h 30m')     // 5400000
parseDuration('2d')         // 172800000

formatDuration(900000)      // '15m'
formatDuration(5400000)     // '1h 30m'

IP Utilities

import { getClientIp, anonymizeIp, isPrivateIp, isLocalhost } from 'nextjs-secure'

// Extract client IP
const ip = getClientIp(request, {
  trustProxy: true,
  customHeaders: ['x-custom-ip'],
})

// Anonymize for GDPR
anonymizeIp('192.168.1.100')  // '192.168.1.xxx'

// Check IP type
isPrivateIp('192.168.1.1')    // true
isPrivateIp('8.8.8.8')        // false
isLocalhost('127.0.0.1')      // true

API Reference

Rate Limiting

| Function | Description | |----------|-------------| | withRateLimit(handler, config) | Wrap handler with rate limiting | | createRateLimiter(config) | Create reusable rate limiter | | checkRateLimit(request, config) | Manual rate limit check | | getRateLimitStatus(key, config) | Get current status without incrementing | | resetRateLimit(key, config) | Reset rate limit for key |

CSRF

| Function | Description | |----------|-------------| | withCSRF(handler, config) | Wrap handler with CSRF protection | | generateCSRF(config) | Generate CSRF token and cookie | | validateCSRF(request, config) | Manual CSRF validation |

Security Headers

| Function | Description | |----------|-------------| | withSecurityHeaders(handler, config) | Add security headers | | createSecurityHeaders(config) | Create headers object | | buildCSP(config) | Build CSP header string | | getPreset(name) | Get preset configuration |

Authentication

| Function | Description | |----------|-------------| | withJWT(handler, config) | JWT authentication | | withAPIKey(handler, config) | API key authentication | | withSession(handler, config) | Session authentication | | withAuth(handler, config) | Combined authentication | | withRoles(handler, config) | Role-based access control | | withOptionalAuth(handler, config) | Optional authentication | | verifyJWT(token, config) | Verify JWT token | | decodeJWT(token) | Decode JWT without verification |

Validation

| Function | Description | |----------|-------------| | withValidation(handler, config) | Schema validation | | withXSSProtection(handler) | Block XSS attempts | | withSanitization(handler, config) | Sanitize input | | withSQLProtection(handler, config) | Block SQL injection | | withFileValidation(handler, config) | File upload validation | | sanitize(input, config) | Manual sanitization | | detectXSS(input) | Detect XSS patterns | | detectSQLInjection(input) | Detect SQL injection | | validatePath(path, config) | Validate file path |

Audit Logging

| Function | Description | |----------|-------------| | withAuditLog(handler, config) | Request logging | | withRequestId(handler, config) | Add request ID | | withTiming(handler, config) | Add response timing | | createSecurityTracker(config) | Create event tracker | | trackSecurityEvent(store, event) | Track single event | | redactObject(obj, config) | Redact PII from object |

Bot Detection

| Function | Description | |----------|-------------| | withBotProtection(handler, config) | Combined bot protection | | withUserAgentProtection(handler, config) | User-agent only protection | | withHoneypotProtection(handler, config) | Honeypot only protection | | withBehaviorProtection(handler, config) | Behavior analysis only | | withCaptchaProtection(handler, config) | CAPTCHA verification | | withBotProtectionPreset(handler, preset) | Use preset configuration | | detectBot(request, config) | Manual bot detection | | analyzeUserAgent(userAgent, config) | Analyze user-agent string | | checkHoneypot(request, config) | Check honeypot fields | | checkBehavior(request, config) | Check request behavior | | verifyCaptcha(token, config) | Verify CAPTCHA token | | generateHoneypotHTML(config) | Generate honeypot HTML | | generateHoneypotCSS(config) | Generate honeypot CSS |


Examples

Complete API with All Security Features

// lib/security.ts
import { createRateLimiter, MemoryStore } from 'nextjs-secure/rate-limit'
import { createSecurityTracker, MemoryStore as AuditStore } from 'nextjs-secure/audit'

export const apiLimiter = createRateLimiter({
  limit: 100,
  window: '15m',
  store: new MemoryStore(),
})

export const strictLimiter = createRateLimiter({
  limit: 5,
  window: '1m',
})

export const auditStore = new AuditStore({ maxEntries: 10000 })
export const securityTracker = createSecurityTracker({ store: auditStore })
// app/api/users/route.ts
import { withJWT, withRoles } from 'nextjs-secure/auth'
import { withValidation } from 'nextjs-secure/validation'
import { withAuditLog } from 'nextjs-secure/audit'
import { apiLimiter, auditStore, securityTracker } from '@/lib/security'

const createUserSchema = {
  email: { type: 'email', required: true },
  name: { type: 'string', minLength: 2, maxLength: 100 },
  role: { type: 'string', enum: ['user', 'admin'] },
}

async function createUser(req, ctx) {
  const { email, name, role } = ctx.validated
  const user = await db.users.create({ email, name, role })
  return Response.json(user, { status: 201 })
}

export const POST = withAuditLog(
  apiLimiter(
    withJWT(
      withRoles(
        withValidation(createUser, { body: createUserSchema }),
        { roles: ['admin'] }
      ),
      { secret: process.env.JWT_SECRET }
    )
  ),
  {
    store: auditStore,
    include: { ip: true, userAgent: true },
    pii: { fields: ['password'], mode: 'remove' },
  }
)

Tiered Rate Limiting

import { createRateLimiter, createUpstashStore } from 'nextjs-secure/rate-limit'

const store = createUpstashStore({
  url: process.env.UPSTASH_REDIS_REST_URL,
  token: process.env.UPSTASH_REDIS_REST_TOKEN,
})

const freeLimiter = createRateLimiter({
  limit: 100,
  window: '1d',
  store,
  identifier: (req) => `free:${req.headers.get('x-api-key')}`,
})

const proLimiter = createRateLimiter({
  limit: 10000,
  window: '1d',
  store,
  identifier: (req) => `pro:${req.headers.get('x-api-key')}`,
})

export async function GET(req) {
  const tier = await getUserTier(req)
  const limiter = tier === 'pro' ? proLimiter : freeLimiter
  return limiter(handler)(req)
}

Roadmap

  • [x] v0.1.x - Rate Limiting
  • [x] v0.2.0 - CSRF Protection
  • [x] v0.3.0 - Security Headers
  • [x] v0.4.0 - Authentication
  • [x] v0.5.0 - Input Validation
  • [x] v0.6.0 - Audit Logging
  • [x] v0.7.0 - Bot Detection

See ROADMAP.md for detailed progress and future plans.


Contributing

Contributions are welcome! Please read CONTRIBUTING.md for details.

# Clone
git clone https://github.com/alpgurlee/next-secure.git
cd next-secure

# Install
npm install

# Test
npm test

# Build
npm run build

Running Tests

npm run test        # Watch mode
npm run test:run    # Single run
npm run test:coverage  # With coverage

License

MIT License - see LICENSE for details.


Made with security in mind for the Next.js community.