NextRush
API ReferenceMiddleware

@nextrush/helmet

Security headers middleware with CSP, HSTS, and modern browser protections.

Why Helmet Exists

When your server responds to a browser request, HTTP headers control how the browser handles the content. Security-critical headers tell the browser:

  • Which scripts are allowed to execute
  • Whether your page can be embedded in an iframe
  • Whether to enforce HTTPS
  • What information to send in the Referer header

Without these headers, browsers default to permissive behavior — allowing cross-site scripting, clickjacking, MIME sniffing, and protocol downgrade attacks.

Helmet sets 13 security response headers with OWASP-recommended values in a single middleware call. You get protection against the most common browser-based attack vectors without configuring each header individually.

What Helmet Protects Against

AttackHeaderWhat Happens Without It
XSS (cross-site scripting)Content-Security-PolicyAttackers inject scripts into your page
ClickjackingX-Frame-OptionsYour site is embedded in a hidden iframe to trick users
MIME sniffingX-Content-Type-OptionsBrowsers execute disguised files as scripts
Protocol downgradeStrict-Transport-SecurityAttackers force HTTP to intercept traffic
Info leakageReferrer-PolicyInternal URLs leak to third parties
FingerprintingRemoves X-Powered-ByAttackers identify your tech stack
Cross-origin data leaksCross-Origin-Opener-PolicyMalicious popups access your page's data

What Helmet Does NOT Do

Helmet handles browser-side response headers only. These concerns need separate middleware:

ConcernPackage
Input validationZod, ArkType, or similar
AuthenticationJWTs, OAuth, or session auth
CSRF protection@nextrush/csrf
Rate limiting@nextrush/rate-limit
CORS@nextrush/cors

Default Behavior

Calling helmet() with no arguments sets 13 security headers covering XSS, clickjacking, MIME sniffing, and protocol downgrades.

Defaults cover common browser risks

A single app.use(helmet()) call protects against the most common browser-based attack vectors. Customize individual headers only when your application requires it.

Full default headers reference
HeaderDefault ValueProtection
Content-Security-PolicyOWASP defaultsXSS, injection attacks
Cross-Origin-Embedder-Policyrequire-corpEmbedding control
Cross-Origin-Opener-Policysame-originCross-origin isolation
Cross-Origin-Resource-Policysame-originResource access control
Strict-Transport-Securitymax-age=15552000; includeSubDomainsForce HTTPS
X-Content-Type-OptionsnosniffMIME sniffing
X-Frame-OptionsSAMEORIGINClickjacking
X-XSS-Protection0Disable legacy XSS filter (OWASP recommended)
X-DNS-Prefetch-ControloffDNS prefetch control
X-Download-OptionsnoopenIE download execution
X-Permitted-Cross-Domain-PoliciesnoneAdobe cross-domain
Referrer-Policyno-referrerInformation leakage
Origin-Agent-Cluster?1Process isolation

Headers not set by default: Permissions-Policy, Clear-Site-Data, Reporting-Endpoints.


Installation

$ pnpm add @nextrush/helmet

Minimal Usage

import { createApp } from '@nextrush/core';
import { helmet } from '@nextrush/helmet';

const app = createApp();
app.use(helmet());

Configuration Options

Customize individual headers by passing options. Disable any header by setting it to false.

app.use(
  helmet({
    contentSecurityPolicy: {
      directives: {
        'default-src': ["'self'"],
        'script-src': ["'self'", 'https://cdn.example.com'],
        'style-src': ["'self'", "'unsafe-inline'"],
        'img-src': ["'self'", 'data:', 'https:'],
      },
    },
    hsts: {
      maxAge: 31536000,
      includeSubDomains: true,
      preload: true,
    },
    frameguard: 'DENY',
    referrerPolicy: 'strict-origin-when-cross-origin',
  })
);

HelmetOptions

PropertyTypeDescription
contentSecurityPolicyContentSecurityPolicyOptions | false= { useDefaults: true }CSP header configuration
crossOriginEmbedderPolicy'require-corp' | 'credentialless' | 'unsafe-none' | false= 'require-corp'Cross-Origin-Embedder-Policy header
crossOriginOpenerPolicy'same-origin' | 'same-origin-allow-popups' | 'unsafe-none' | false= 'same-origin'Cross-Origin-Opener-Policy header
crossOriginResourcePolicy'same-origin' | 'same-site' | 'cross-origin' | false= 'same-origin'Cross-Origin-Resource-Policy header
dnsPrefetchControl'on' | 'off' | false= 'off'X-DNS-Prefetch-Control header
frameguard'DENY' | 'SAMEORIGIN' | false= 'SAMEORIGIN'X-Frame-Options header
hstsStrictTransportSecurityOptions | false= { maxAge: 15552000, includeSubDomains: true }Strict-Transport-Security header
noSniffboolean= trueX-Content-Type-Options nosniff header
originAgentClusterboolean= trueOrigin-Agent-Cluster header
permissionsPolicy?PermissionsPolicyDirectives | false= undefined (not set)Permissions-Policy header
referrerPolicyReferrerPolicyValue | ReferrerPolicyValue[] | false= 'no-referrer'Referrer-Policy header
xssFilterboolean= falseX-XSS-Protection header. false sets '0' (recommended), true sets '1; mode=block'
ieNoOpenboolean= trueX-Download-Options noopen header (IE)
permittedCrossDomainPolicies'none' | 'master-only' | 'by-content-type' | 'all' | false= 'none'X-Permitted-Cross-Domain-Policies header
clearSiteData?ClearSiteDataValue[] | falseClear-Site-Data header (useful for logout endpoints)
reportingEndpoints?Record<string, string> | falseReporting-Endpoints header

StrictTransportSecurityOptions

PropertyTypeDescription
maxAgenumber= 15552000 (180 days)Max age in seconds
includeSubDomainsboolean= trueApply to all subdomains
preloadboolean= falseAllow HSTS preload list submission

ContentSecurityPolicyOptions

PropertyTypeDescription
directives?ContentSecurityPolicyDirectivesCSP directives using hyphenated keys
reportOnlyboolean= falseUse Content-Security-Policy-Report-Only header
useDefaultsboolean= trueMerge with OWASP default directives

Presets

Built-in configurations for common scenarios:

import { strictHelmet, apiHelmet, devHelmet, staticHelmet, logoutHelmet } from '@nextrush/helmet';

// Maximum security for production apps handling sensitive data
app.use(strictHelmet());

// Optimized for REST/GraphQL APIs (disables CSP, COEP)
app.use(apiHelmet());

// Relaxed headers for local development (disables HSTS, CSP)
app.use(devHelmet());

// Optimized for static file serving
app.use(staticHelmet());

// Clears browser cache, cookies, and storage on logout
app.post('/logout', logoutHelmet());

Each preset accepts an overrides parameter (except devHelmet):

app.use(
  strictHelmet({
    hsts: { maxAge: 63072000, includeSubDomains: true, preload: true },
  })
);

Content Security Policy

CSP directives use hyphenated keys matching the HTTP header specification.

Basic CSP

app.use(
  helmet({
    contentSecurityPolicy: {
      directives: {
        'default-src': ["'self'"],
        'script-src': ["'self'", 'https://cdn.example.com'],
        'style-src': ["'self'", "'unsafe-inline'"],
        'img-src': ["'self'", 'data:', 'https:'],
        'connect-src': ["'self'", 'https://api.example.com'],
        'object-src': ["'none'"],
      },
    },
  })
);

CSP Builder

Fluent API for building CSP headers:

import { createCspBuilder } from '@nextrush/helmet';

const builder = createCspBuilder()
  .defaultSrc("'self'")
  .scriptSrc("'self'", 'https://cdn.example.com')
  .styleSrc("'self'", "'unsafe-inline'")
  .imgSrc("'self'", 'data:', 'https:')
  .upgradeInsecureRequests();

app.use(
  helmet({
    contentSecurityPolicy: builder.toOptions(),
  })
);

CSP with Nonces

For inline scripts without 'unsafe-inline':

import { buildCspWithNonce, buildCspHeader, generateNonce } from '@nextrush/helmet';

app.use(async (ctx) => {
  const nonce = generateNonce();
  ctx.state.cspNonce = nonce;

  const { cspOptions } = buildCspWithNonce(
    {
      directives: {
        'default-src': ["'self'"],
        'script-src': ["'self'"],
      },
      generateNonce: true,
    },
    nonce
  );

  const cspHeader = buildCspHeader(cspOptions.directives ?? {});
  ctx.set('Content-Security-Policy', cspHeader);
  await ctx.next();
});

Permissions Policy

Control browser feature access. Pass PermissionsPolicyDirectives directly — use an empty array to disable a feature, and ['self'] for self-only access.

app.use(
  helmet({
    permissionsPolicy: {
      camera: [],
      microphone: [],
      geolocation: ['self'],
      fullscreen: ['self'],
      payment: ['self', 'https://pay.example.com'],
    },
  })
);

Use the builder for complex policies:

import { createPermissionsPolicyBuilder } from '@nextrush/helmet';

const policy = createPermissionsPolicyBuilder()
  .disable('camera', 'microphone', 'geolocation')
  .allow('fullscreen', 'self')
  .allow('payment', 'self', 'https://pay.example.com')
  .getDirectives();

app.use(helmet({ permissionsPolicy: policy }));

Use restrictivePermissionsPolicy() for a preset that disables most features:

import { restrictivePermissionsPolicy } from '@nextrush/helmet';

app.use(
  helmet({
    permissionsPolicy: restrictivePermissionsPolicy(),
  })
);

Individual Header Middleware

Apply specific headers without the full Helmet stack:

import { contentSecurityPolicy, hsts, frameguard, noSniff, referrerPolicy } from '@nextrush/helmet';

app.use(
  contentSecurityPolicy({
    directives: { 'default-src': ["'self'"] },
  })
);

app.use(hsts({ maxAge: 31536000, includeSubDomains: true, preload: true }));

app.use(frameguard('DENY'));

app.use(noSniff());

app.use(referrerPolicy('strict-origin-when-cross-origin'));

Disabling Headers

Set any header option to false to skip it:

app.use(
  helmet({
    contentSecurityPolicy: false,
    hsts: false,
  })
);

Common Mistakes

These are the most frequent configuration errors when using Helmet.

CSP directives use hyphenated keys like 'default-src', not camelCase like defaultSrc. Using camelCase produces invalid headers silently.

Using frameguard with an objectframeguard takes a string ('DENY' or 'SAMEORIGIN'), not { action: 'deny' }.

Enabling hsts.preload prematurely — HSTS preload is permanent. Only enable after submitting to the HSTS Preload List. Preload requires maxAge >= 31536000 and includeSubDomains: true.

Expecting Permissions-Policy by default — Helmet does not set Permissions-Policy unless you provide permissionsPolicy in the options.


Troubleshooting

Cross-origin resources fail to load — The default Cross-Origin-Embedder-Policy: require-corp blocks resources without CORP headers. Set crossOriginEmbedderPolicy: false or 'credentialless' if loading third-party resources.

CORS requests blocked after adding HelmetCross-Origin-Resource-Policy: same-origin blocks cross-origin resource sharing. Set crossOriginResourcePolicy: 'cross-origin' for public resources.

Inline scripts blocked by CSP — Default CSP does not allow 'unsafe-inline' for scripts. Use nonces or add the source to 'script-src'.


On this page