Skip to content

Omnifolio-app/nextjs-security-headers

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

@omnifolio/nextjs-security-headers

npm version License: MIT TypeScript

Drop-in Next.js middleware that adds CSP with per-request nonces, HSTS, Permissions-Policy, CORS allowlist, X-Frame-Options, and more. Works with App Router and Edge Runtime.

Built by OmniFolio — Financial Intelligence Platform.


Features

  • 🔒 CSP with nonces — per-request nonce for inline scripts, strict-dynamic for chunk loading
  • 🌐 CORS allowlist — strict origin matching with regex support
  • 🛡️ HSTS — 1-year max-age with preload by default
  • 📋 Permissions-Policy — 22 features locked down out of the box
  • 🚫 X-Frame-Options — clickjacking protection
  • API route optimization — skips browser-only headers for API routes
  • 🧩 Framework-agnostic coreapplySecurityHeaders() works with any Headers object
  • 📝 Report-only mode — test CSP without breaking your site

Install

npm install @omnifolio/nextjs-security-headers

Quick Start

// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
import {
  applySecurityHeaders,
  generateNonce,
  isApiPath,
} from '@omnifolio/nextjs-security-headers';

export function middleware(request: NextRequest) {
  const pathname = request.nextUrl.pathname;
  const isApi = isApiPath(pathname);
  const nonce = isApi ? '' : generateNonce();

  const response = NextResponse.next({
    request: { headers: new Headers(request.headers) },
  });

  applySecurityHeaders(response.headers, {
    nonce,
    isApiRoute: isApi,
    isDev: process.env.NODE_ENV === 'development',
    pathname,
    origin: request.headers.get('origin'),
    config: {
      cors: {
        allowedOrigins: [
          'https://www.myapp.com',
          'https://myapp.com',
          /^https:\/\/[a-z0-9-]+\.run\.app$/,  // Cloud Run previews
        ],
      },
      csp: {
        connectSrc: ["'self'", 'https://api.stripe.com'],
        frameSrc: ["'self'", 'https://js.stripe.com'],
      },
      scriptSources: [
        'https://js.stripe.com',
      ],
    },
  });

  return response;
}

export const config = {
  matcher: ['/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)'],
};

Using the Nonce in Server Components

// app/layout.tsx
import { headers } from 'next/headers';

export default async function RootLayout({ children }) {
  const nonce = (await headers()).get('x-nonce') || '';

  return (
    <html>
      <head>
        <script nonce={nonce} src="..." />
      </head>
      <body>{children}</body>
    </html>
  );
}

Configuration

CSP Directives

{
  csp: {
    defaultSrc: ["'self'"],
    scriptSrc: ["'self'", "https://cdn.example.com"],
    styleSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com"],
    imgSrc: ["'self'", "data:", "https:"],
    connectSrc: ["'self'", "https://api.example.com"],
    frameSrc: ["'self'", "https://www.youtube.com"],
    reportUri: "https://csp-report.example.com/report",
  },
  cspMode: 'enforce',         // 'enforce' | 'report-only' | 'off'
  scriptSources: [...],       // Shorthand for additional script-src entries
}

CORS

{
  cors: {
    allowedOrigins: [
      'https://www.myapp.com',
      /^https:\/\/deploy-preview-\d+\.netlify\.app$/,
    ],
    methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
    headers: ['Content-Type', 'Authorization', 'X-CSRF-Token'],
    credentials: true,
  },
}

HSTS

{
  hstsMaxAge: 31536000,          // 1 year (default)
  hstsIncludeSubDomains: true,   // default
  hstsPreload: true,             // default
  disableHSTS: false,            // set true to disable
}

Permissions-Policy

{
  permissionsPolicy: {
    camera: '(self)',              // Override default '()'
    geolocation: '(self)',         // Already default
    payment: '(self "https://js.stripe.com")',
  },
}

API Reference

applySecurityHeaders(headers, options)

Core function — applies all security headers to a Headers object.

buildCSP(nonce, config?)

Build a CSP string with nonce injected into script-src.

buildPermissionsPolicy(overrides?)

Build a Permissions-Policy header value.

generateNonce()

Generate a cryptographically random base64 nonce.

isApiPath(pathname, prefixes?)

Check if a path matches API route prefixes.

isOriginAllowed(origin, corsConfig)

Check if an origin is in the CORS allowlist.

Default Security Headers

Header Value
Content-Security-Policy Full CSP with nonce + strict-dynamic
X-Content-Type-Options nosniff
X-Frame-Options DENY
X-XSS-Protection 1; mode=block
Referrer-Policy strict-origin-when-cross-origin
Strict-Transport-Security max-age=31536000; includeSubDomains; preload
Permissions-Policy 22 features locked down

License

MIT — see LICENSE.


Built with ❤️ by OmniFolio

About

Drop-in Next.js middleware for CSP with per-request nonces, HSTS, Permissions-Policy, CORS, and security headers. App Router + Edge Runtime.

Topics

Resources

License

Contributing

Security policy

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors