Package Exports
- headyx
- headyx/dist/index.cjs
- headyx/dist/index.mjs
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (headyx) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
headyx
Production-ready security headers configuration for Next.js applications
headyx is a comprehensive, zero-configuration security headers library designed specifically for Next.js applications. It provides production-ready security headers with sensible defaults, extensive customization options, and beginner-friendly documentation.
Table of Contents
- headyx
- Table of Contents
- Installation
- Quick Start
- What Are Security Headers?
- Default Security Headers
- Configuration Options
- Complete Configuration Examples
- Common Use Cases
- Troubleshooting
- Future Plans
- Contributing
- License
Installation
# Using npm
npm install headyx
# Using pnpm (recommended)
pnpm add headyx
# Using yarn
yarn add headyxRequirements:
- Node.js 18.0.0 or higher
- Next.js 12.0.0 or higher
- TypeScript 5.0.0 or higher (optional, for type definitions)
Quick Start
Install the package:
pnpm add headyx
Add to your Next.js configuration:
Create or update
next.config.ts(ornext.config.js):import { headers } from 'headyx'; export default { poweredByHeader: false, // Recommended: remove X-Powered-By header headers: headers(), };
That's it! Your Next.js app now has production-ready security headers.
The default configuration provides strong security out of the box. You can customize it later if needed.
Alternative: Use Standalone Functions
Instead of using headers(), you can also use the individual composable functions directly in your Next.js middleware or anywhere else:
import { cors, csp, hsts, crossOrigin, permissionsPolicy, other } from 'headyx';
// Use them individually - plug and play!
const corsHeaders = cors({ origin: 'https://app.example.com' });
const { header, value } = csp({ addScriptSrc: ['https://cdn.example.com'] });
const hstsHeader = hsts({ enabled: true });
// ... etcSee Using Composable Functions Directly for more details.
What Are Security Headers?
Security headers are HTTP response headers that tell browsers how to handle your website. They protect against common web vulnerabilities like:
- Cross-Site Scripting (XSS): Attackers injecting malicious scripts into your pages
- Clickjacking: Attackers embedding your site in an iframe to trick users
- Man-in-the-Middle Attacks: Attackers intercepting data between users and your server
- Data Injection: Attackers injecting malicious code through forms or APIs
- Privacy Violations: Unauthorized access to user's camera, microphone, location, etc.
Think of security headers as instructions for browsers - they tell the browser "only load scripts from these trusted sources," "always use HTTPS," "don't allow this page to be embedded in iframes," etc.
Default Security Headers
When you call headers() without any configuration, you get these production-ready security headers:
Content Security Policy (CSP)
- Default sources:
'self'(same origin) - Base URI:
'self'(restricts<base>tag) - Form action:
'self'(restricts form submissions) - Frame ancestors:
'none'(prevents embedding in iframes) - Object source:
'none'(blocks<object>,<embed>,<applet>) - Worker source:
'self',blob: - Scripts:
'self',https:,'unsafe-inline'(for Next.js compatibility) - Styles:
'self',https:,'unsafe-inline'(for Next.js compatibility) - Images:
'self',data:,blob:,https: - Fonts:
'self',data:,https: - Connections:
'self',https:,wss: - Frames:
'self',https: - Upgrade insecure requests: Enabled (forces HTTPS)
HTTP Strict Transport Security (HSTS)
- Enabled: Yes
- Max-Age: 2 years (63,072,000 seconds)
- Include Subdomains: Yes
- Preload: No (you can enable if submitting to HSTS preload list)
Cross-Origin Policies
- COOP:
same-origin(prevents cross-origin window access) - CORP:
same-origin(blocks cross-origin resource requests) - COEP: Not enabled (enable only if you need cross-origin isolation)
Permissions Policy
- Geolocation: Disabled
- Microphone: Disabled
- Camera: Disabled
- Browsing Topics API: Disabled
Other Headers
- Referrer-Policy:
strict-origin-when-cross-origin(privacy-friendly) - X-Frame-Options:
DENY(prevents iframe embedding) - X-Content-Type-Options:
nosniff(prevents MIME sniffing) - X-DNS-Prefetch-Control:
off(privacy-first) - Origin-Agent-Cluster: Enabled (process isolation)
All of these are applied automatically with zero configuration!
Configuration Options
All configuration options are optional. You only need to configure what you want to customize. The defaults are secure and production-ready.
Content Security Policy (CSP)
Content Security Policy is one of the most powerful security headers. It controls where browsers can load resources from (scripts, styles, images, etc.) and prevents XSS attacks.
CSP Directives
You can add additional sources to CSP directives:
import { headers } from 'headyx';
export default {
headers: headers({
csp: {
// Allow scripts from a CDN
addScriptSrc: ['https://cdn.example.com'],
// Allow API calls to your backend
addConnectSrc: ['https://api.example.com', 'wss://ws.example.com'],
// Allow stylesheets from Google Fonts
addStyleSrc: ['https://fonts.googleapis.com'],
// Allow images from an image CDN
addImgSrc: ['https://images.unsplash.com'],
// Allow fonts from Google Fonts
addFontSrc: ['https://fonts.gstatic.com'],
// Allow iframes from YouTube, Vimeo, etc.
addFrameSrc: ['https://www.youtube.com', 'https://player.vimeo.com'],
},
}),
};CSP Keywords and Schemes
You can use special keywords and schemes in CSP sources:
Keywords:
'self': Same origin (your domain)'unsafe-inline': Allows inline scripts/styles (weakens security)'unsafe-eval': Allowseval()(very dangerous, avoid)'wasm-unsafe-eval': Allows WebAssembly compilation (safer than'unsafe-eval')'strict-dynamic': Modern CSP pattern for scripts'report-sample': Include code sample in violation reports'unsafe-hashes': Allows event handlers and style attributes with matching hashes
Schemes:
https:: Any HTTPS URLhttp:: Any HTTP URL (less secure)data:: Data URIs (e.g.,data:image/png;base64,...)blob:: Blob URLs created by the browserfilesystem:: File system URLsmediastream:: Media stream URLswss:: WebSocket over HTTPS (preferred)ws:: WebSocket over HTTP
Example:
csp: {
addScriptSrc: [
"'self'", // Same origin
"https://cdn.com", // Specific CDN
"'strict-dynamic'", // Modern CSP pattern
],
}Strict Inline Control
By default, CSP includes 'unsafe-inline' for Next.js compatibility. For better security, you can remove it and use nonces or hashes instead.
Nonces (recommended for dynamic content):
- Random values generated per request
- Must match between CSP header and HTML attribute
- Use for dynamic inline scripts/styles
csp: {
strictInline: {
// Use a placeholder that you'll replace at runtime
scriptNonces: ["nonce-__NONCE__"],
styleNonces: ["nonce-__NONCE__"],
},
}In your Next.js middleware or runtime:
import { generateNonce } from 'some-crypto-library';
const nonce = generateNonce(); // Generate per request
const csp = cspValue.replace('__NONCE__', nonce);Hashes (for static content):
- SHA-256/384/512 hashes of the exact script/style content
- Pre-computed and added to CSP
- Use for static inline scripts/styles
csp: {
strictInline: {
// Pre-computed hash of your inline script
scriptHashes: ["sha256-abc123..."],
styleHashes: ["sha256-xyz789..."],
},
}Important: When you use strictInline, you must provide nonces or hashes. Without them, all inline scripts/styles will be blocked!
Trusted Types
Trusted Types prevent DOM-based XSS attacks by enforcing that dangerous DOM APIs only accept "trusted" values.
Warning: This requires implementing Trusted Types policies in your JavaScript code. Without policies, your app will break!
csp: {
trustedTypes: {
requireForScript: true, // Enforce for script execution
policies: ["default", "sanitize"], // Policy names you'll create
},
}In your JavaScript:
// Create Trusted Types policies
if (typeof trustedTypes !== 'undefined') {
trustedTypes.createPolicy('sanitize', {
createHTML: (input) => DOMPurify.sanitize(input),
});
}CSP Violation Reporting
You can configure CSP to report violations to your endpoint:
csp: {
reporting: {
endpoints: [
{ name: "csp", url: "https://report.example.com/csp" },
],
cspGroup: "csp", // Reference to the endpoint group
},
reportOnly: false, // Set to true to test without blocking
}Report-Only Mode:
Use reportOnly: true to test your CSP policy without blocking anything. Violations are reported but not blocked. Once you verify everything works, set it to false to enforce the policy.
Note: When using reportOnly: true, the header name changes from Content-Security-Policy to Content-Security-Policy-Report-Only.
Cross-Origin Resource Sharing (CORS)
CORS headers control which origins can access your API from a browser. This configuration is static - it applies the same headers to all requests.
Important: Never combine origin: "*" with credentials: true. Browsers will reject this combination!
cors: {
// Single origin (required when using credentials)
origin: "https://app.example.com",
// Or allow any origin (cannot use with credentials)
// origin: "*",
methods: ["GET", "POST", "OPTIONS"],
allowedHeaders: ["content-type", "authorization"],
exposedHeaders: ["x-total-count"],
credentials: true, // Allow cookies/auth headers
maxAge: 600, // Cache preflight for 10 minutes
varyOrigin: true, // Ensure cache safety
}For dynamic CORS (different origins per request), use Next.js middleware with the cors() composable function instead of this static configuration.
Note: When using multiple origins in an array, the function will normalize to "*" since browsers only support one origin in the CORS header. For true multi-origin support, implement dynamic CORS in middleware.
HTTP Strict Transport Security (HSTS)
HSTS forces browsers to use HTTPS for all future requests to your domain. This prevents protocol downgrade attacks and cookie hijacking.
hsts: true, // Enable HSTS
hstsMaxAge: 63072000, // 2 years (default)
hstsIncludeSubDomains: true, // Apply to all subdomains
hstsPreload: false, // Only enable if submitting to preload listHSTS Preload:
Only enable hstsPreload: true if you plan to submit your domain to the HSTS preload list (like Chrome's). Requirements:
hstsMaxAgemust be at least 1 year (31,536,000 seconds)hstsIncludeSubDomainsmust betrue- All subdomains must support HTTPS
- Root domain must redirect HTTP to HTTPS
Once on the preload list, removal is difficult. Only enable if you're committed to HTTPS forever.
Cross-Origin Policies
Cross-Origin-Opener-Policy (COOP)
Controls how your page can be opened in popups and how it can access other windows.
coop: "same-origin", // Most secure (default)
// coop: "same-origin-allow-popups", // Allow popups
// coop: "unsafe-none", // No restrictions (least secure)Note: If coep is enabled, coop must not be "unsafe-none" (will be automatically changed to "same-origin" by the implementation).
Cross-Origin-Resource-Policy (CORP)
Controls whether the browser blocks cross-origin requests to your resources.
corp: "same-origin", // Most restrictive (default)
// corp: "same-site", // Allow same-site requests
// corp: "cross-origin", // Allow all cross-origin requestsCross-Origin-Embedder-Policy (COEP)
Enables cross-origin isolation, which allows access to powerful APIs like SharedArrayBuffer and performance.measureUserAgentSpecificMemory().
Warning: Enabling this requires ALL embedded resources (iframes, images, scripts, etc.) to opt-in via CORS headers or CORP. This can break third-party embeds!
// Only enable if you need cross-origin isolation
coep: "require-corp", // All embedded resources must send CORP/CORS
// coep: "credentialless", // Embed without credentials (newer, less strict)Permissions Policy
Controls which browser features and APIs can be used. By default, sensitive features are disabled.
permissions: {
// Disable features (empty array = disabled)
geolocation: [],
microphone: [],
camera: [],
"browsing-topics": [],
// Allow for same-origin only
camera: ["self"],
// Allow for specific origins
microphone: ["self", "https://trusted-site.com"],
// Allow autoplay for same-origin
autoplay: ["self"],
}Available Features:
accelerometer- Device accelerometer accessambient-light-sensor- Ambient light sensor accessautoplay- Automatic media playbackbattery- Battery Status APIbrowsing-topics- Privacy Sandbox Topics APIcamera- Camera accessclipboard-read- Clipboard read accessclipboard-write- Clipboard write accessdisplay-capture- Screen capture/sharingdocument-domain- Document.domain manipulationencrypted-media- Encrypted Media Extensionsfullscreen- Fullscreen APIgamepad- Gamepad APIgeolocation- Geolocation APIgyroscope- Device gyroscope accesshid- WebHID APIidle-detection- Idle Detection APIkeyboard-map- Keyboard Map APImagnetometer- Device magnetometer accessmicrophone- Microphone accessmidi- Web MIDI APIpayment- Payment Request APIpicture-in-picture- Picture-in-Picture APIpublickey-credentials-get- WebAuthn APIscreen-wake-lock- Screen Wake Lock APIserial- Web Serial APIspeaker-selection- Speaker selectionstorage-access- Storage Access APIusb- WebUSB APIweb-share- Web Share APIxr-spatial-tracking- WebXR spatial tracking
Important: Only disable features you don't use. Disabling features you rely on will break your application!
Other Security Headers
Referrer-Policy
Controls how much referrer information is sent with requests.
referrerPolicy: "strict-origin-when-cross-origin", // Default (privacy-friendly)
// referrerPolicy: "no-referrer", // Never send referrer (most private)
// referrerPolicy: "same-origin", // Send only for same-origin requestsX-Frame-Options
Legacy header that controls whether your page can be embedded in iframes.
xFrameOptions: "DENY", // Default (prevents iframe embedding)
// xFrameOptions: "SAMEORIGIN", // Allow same-origin iframesNote: Modern CSP frame-ancestors directive (set to 'none' by default) is preferred, but this header provides compatibility with older browsers.
X-DNS-Prefetch-Control
Controls whether browsers should prefetch DNS for links on the page.
xDnsPrefetch: "off", // Default (privacy-first)
// xDnsPrefetch: "on", // Enable for faster navigationOrigin-Agent-Cluster
Isolates your origin in its own process/thread. Improves security and performance.
originAgentCluster: true, // Default (enabled)X-Permitted-Cross-Domain-Policies
Legacy header for Adobe Flash and Silverlight. Most modern applications don't need this since Flash is obsolete. Some organizations still require it for compliance reasons.
xPermittedCrossDomainPolicies: "none", // Disable cross-domain policiesCustom Headers
Add custom headers that aren't covered by built-in options:
extra: [
{ key: "X-Custom-Header", value: "custom-value" },
{ key: "X-API-Version", value: "v1" },
],Path Matching
Control which routes the headers are applied to:
paths: ["/(.*)"], // Default (all routes)
// paths: ["/api/*", "/admin/*"], // Specific routes onlyUsing Composable Functions Directly
You don't need to use headers() - you can use the standalone functions directly! These composable functions are completely independent and can be used anywhere - in Next.js middleware, Express middleware, Fastify plugins, or any other context.
Each function is plug-and-play and works independently:
import { cors, csp, hsts, crossOrigin, permissionsPolicy, other } from 'headyx';
// In Next.js middleware
export function middleware(request: NextRequest) {
const response = NextResponse.next();
// CORS headers
const corsHeaders = cors({ origin: 'https://app.example.com' });
corsHeaders.forEach((header) => {
response.headers.set(header.key, header.value);
});
// CSP header
const { header, value, reportingEndpoints } = csp({
addScriptSrc: ['https://cdn.example.com'],
});
response.headers.set(header, value);
if (reportingEndpoints) {
response.headers.set(reportingEndpoints.key, reportingEndpoints.value);
}
// HSTS header
const hstsHeader = hsts({ enabled: true, maxAge: 63072000 });
if (hstsHeader) {
response.headers.set(hstsHeader.key, hstsHeader.value);
}
// Cross-Origin headers
const crossOriginHeaders = crossOrigin({
coop: 'same-origin',
corp: 'same-origin',
});
crossOriginHeaders.forEach((header) => {
response.headers.set(header.key, header.value);
});
// Permissions Policy
const permissionsHeader = permissionsPolicy({ geolocation: [], camera: [] });
if (permissionsHeader) {
response.headers.set(permissionsHeader.key, permissionsHeader.value);
}
// Other headers
const otherHeaders = other({
referrerPolicy: 'strict-origin-when-cross-origin',
xFrameOptions: 'DENY',
});
otherHeaders.forEach((header) => {
response.headers.set(header.key, header.value);
});
return response;
}Available standalone functions (plug and play!):
cors()- CORS headers (returns array of headers)csp()- Content Security Policy (returns{ header, value, reportingEndpoints? })hsts()- HTTP Strict Transport Security (returns header or null)crossOrigin()- Cross-Origin Policies (COOP, CORP, COEP) (returns array of headers)permissionsPolicy()- Permissions Policy (returns header or null)other()- Other security headers (Referrer-Policy, X-Frame-Options, etc.) (returns array of headers)
Import them individually:
import { cors, csp, hsts, crossOrigin, permissionsPolicy, other } from 'headyx';Use cases for standalone functions:
- Next.js middleware - Apply headers conditionally based on request
- Express.js - Use in Express middleware or route handlers
- Fastify - Use in Fastify plugins or hooks
- API routes - Apply different headers to different routes
- Dynamic CORS - Different CORS config per origin
- Conditional headers - Only apply certain headers under specific conditions
Example: Use just CORS in middleware:
import { cors } from 'headyx';
export function middleware(request: NextRequest) {
const response = NextResponse.next();
// Only add CORS headers for API routes
if (request.nextUrl.pathname.startsWith('/api')) {
const corsHeaders = cors({
origin: 'https://app.example.com',
credentials: true,
});
corsHeaders.forEach((header) => {
response.headers.set(header.key, header.value);
});
}
return response;
}Complete Configuration Examples
Basic Setup (Zero Config)
The simplest setup - just use the defaults:
// next.config.ts
import { headers } from 'headyx';
export default {
poweredByHeader: false,
headers: headers(),
};This gives you production-ready security headers with zero configuration!
Beginner-Friendly Example
A simple example with common customizations:
// next.config.ts
import { headers } from 'headyx';
export default {
poweredByHeader: false,
headers: headers({
csp: {
// Allow scripts from a CDN
addScriptSrc: ['https://cdn.example.com'],
// Allow API calls
addConnectSrc: ['https://api.example.com'],
// Allow Google Fonts
addStyleSrc: ['https://fonts.googleapis.com'],
addFontSrc: ['https://fonts.gstatic.com'],
},
// HSTS is already enabled by default, but you can customize:
hstsMaxAge: 31536000, // 1 year instead of 2
}),
};Production-Ready Example
A comprehensive example suitable for production:
// next.config.ts
import { headers } from 'headyx';
export default {
poweredByHeader: false,
headers: headers({
csp: {
// Allow third-party resources
addScriptSrc: [
'https://cdn.example.com',
'https://www.googletagmanager.com',
],
addConnectSrc: ['https://api.example.com', 'wss://ws.example.com'],
addStyleSrc: ['https://fonts.googleapis.com'],
addFontSrc: ['https://fonts.gstatic.com'],
addImgSrc: ['https://images.example.com'],
addFrameSrc: ['https://www.youtube.com', 'https://player.vimeo.com'],
// Report CSP violations (optional)
reporting: {
endpoints: [{ name: 'csp', url: 'https://report.example.com/csp' }],
cspGroup: 'csp',
},
},
// CORS for API endpoints
cors: {
origin: 'https://app.example.com',
methods: ['GET', 'POST', 'OPTIONS'],
allowedHeaders: ['content-type', 'authorization'],
credentials: true,
maxAge: 600,
},
// HSTS configuration
hsts: true,
hstsMaxAge: 63072000, // 2 years
hstsIncludeSubDomains: true,
// Permissions Policy
permissions: {
geolocation: [],
microphone: [],
camera: [],
'browsing-topics': [],
},
}),
};Advanced Security Example
Maximum security configuration with strict CSP and Trusted Types:
// next.config.ts
import { headers } from 'headyx';
export default {
poweredByHeader: false,
headers: headers({
csp: {
// Remove 'unsafe-inline' and use nonces instead
strictInline: {
scriptNonces: ['nonce-__NONCE__'], // Replace at runtime
styleNonces: ['nonce-__NONCE__'], // Replace at runtime
},
// Enable Trusted Types for DOM XSS protection
trustedTypes: {
requireForScript: true,
policies: ['default', 'sanitize'],
},
// Allow only necessary sources
addScriptSrc: ['https://cdn.example.com'],
addConnectSrc: ['https://api.example.com'],
// Report violations
reporting: {
endpoints: [{ name: 'csp', url: 'https://report.example.com/csp' }],
cspGroup: 'csp',
},
reportOnly: false, // Enforce policy
},
// Maximum cross-origin restrictions
corp: 'same-origin',
coop: 'same-origin',
// coep: "require-corp", // Only if you need cross-origin isolation
// HSTS with preload
hsts: true,
hstsMaxAge: 31536000, // 1 year minimum for preload
hstsIncludeSubDomains: true,
// hstsPreload: true, // Only if submitting to preload list
// Disable all unnecessary features
permissions: {
geolocation: [],
microphone: [],
camera: [],
'browsing-topics': [],
battery: [],
accelerometer: [],
gyroscope: [],
},
// Privacy-first
referrerPolicy: 'no-referrer',
xDnsPrefetch: 'off',
}),
};Important Notes for Advanced Configuration:
Nonces must be replaced at runtime - Use Next.js middleware or your framework's runtime to replace
__NONCE__with actual nonces.Trusted Types requires policies - You must implement Trusted Types policies in your JavaScript code.
COEP can break third-party embeds - Only enable if you need cross-origin isolation.
HSTS preload is permanent - Once on the preload list, removal is difficult.
Common Use Cases
Adding a CDN
csp: {
addScriptSrc: ["https://cdn.example.com"],
addStyleSrc: ["https://cdn.example.com"],
addImgSrc: ["https://cdn.example.com"],
}Adding Google Fonts
csp: {
addStyleSrc: ["https://fonts.googleapis.com"],
addFontSrc: ["https://fonts.gstatic.com"],
}Adding Analytics
csp: {
addScriptSrc: ["https://www.googletagmanager.com"],
addConnectSrc: ["https://www.google-analytics.com"],
}Adding YouTube/Vimeo Embeds
csp: {
addFrameSrc: [
"https://www.youtube.com",
"https://player.vimeo.com",
],
}API with CORS
cors: {
origin: "https://app.example.com",
methods: ["GET", "POST", "OPTIONS"],
allowedHeaders: ["content-type", "authorization"],
credentials: true,
}Testing CSP Without Breaking
csp: {
reportOnly: true, // Test mode - violations logged but not blocked
reporting: {
endpoints: [{ name: "csp", url: "https://report.example.com/csp" }],
cspGroup: "csp",
},
}Troubleshooting
CSP Violations
Problem: Resources are blocked by CSP.
Solution:
- Check browser console for CSP violation messages
- Add the blocked source to the appropriate CSP directive
- Use
reportOnly: trueto test without blocking - Check CSP violation reports if reporting is configured
CORS Errors
Problem: Cross-origin requests are blocked.
Solution:
- Ensure
corsconfiguration matches your frontend origin - Never combine
origin: "*"withcredentials: true - Check that
allowedHeadersincludes all required headers - For dynamic CORS, use Next.js middleware instead
HSTS Issues
Problem: HSTS is causing issues or you want to remove it.
Solution:
- Set
hsts: falseto disable HSTS - Note: Browsers remember HSTS for the
maxAgeduration - If you're on the preload list, removal is difficult
Third-Party Scripts Not Loading
Problem: Third-party scripts/styles are blocked.
Solution:
- Add the source to
addScriptSrcoraddStyleSrc - Check if the script uses
eval()- you may need'unsafe-eval'(not recommended) - Use
reportOnly: trueto see what's being blocked
Trusted Types Errors
Problem: Trusted Types is blocking your code.
Solution:
- Implement Trusted Types policies in your JavaScript
- Or disable Trusted Types:
trustedTypes: undefined - Check browser console for Trusted Types violation messages
Future Plans
headyx is currently designed for Next.js, but I'm planning to extend support for:
- Express.js - Middleware for Express applications
- Fastify - Plugin for Fastify applications
- Other frameworks - Based on community demand
The API will remain consistent across frameworks, making it easy to migrate between them.
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
MIT © @rccyx