Skip to Content

Security headers

Defensive response headers with sensible defaults.

A handful of response headers switch on protections that browsers leave off by default. securityHeaders sets them on every response.

import { securityHeaders } from '@sukujs/security';

Suku.create({ middlewares: [securityHeaders()] });

What it sets

HeaderDefaultWhat it does
Strict-Transport-Securitymax-age=15552000; includeSubDomainsBrowsers use HTTPS only, for 180 days.
Content-Security-PolicyNothing may load, no framingLimits what a response can do if rendered.
X-Content-Type-OptionsnosniffBrowsers trust the declared content type.
X-Frame-OptionsDENYThe response cannot be framed. Stops clickjacking.
Referrer-Policyno-referrerYour URLs are not leaked to other sites.
Cross-Origin-Opener-Policysame-originOther sites cannot hold a reference to your window.
Cross-Origin-Resource-Policysame-originOther sites cannot embed your responses.

The defaults are tuned for an API that returns JSON. If you serve HTML, set a real content security policy.

A header your handler already set is left alone, so one route can loosen a policy without turning it off for the whole app.

route.get('/embed', {
  handle: () => new Response(html, { headers: { 'content-type': 'text/html', 'x-frame-options': 'SAMEORIGIN' } }),
});

Options

Each header takes a value, or false to omit it.

securityHeaders({
  hsts: { maxAge: 31_536_000, includeSubDomains: true, preload: true },
  frameOptions: 'SAMEORIGIN',
  referrerPolicy: 'strict-origin-when-cross-origin',
  crossOriginResourcePolicy: 'cross-origin',   // for public images or fonts
  permissionsPolicy: 'camera=(), microphone=(), geolocation=()',
  extra: { 'x-robots-tag': 'noindex' },
});
OptionHeader
hstsStrict-Transport-Security
contentSecurityPolicyContent-Security-Policy
noSniffX-Content-Type-Options
frameOptionsX-Frame-Options
referrerPolicyReferrer-Policy
crossOriginOpenerPolicyCross-Origin-Opener-Policy
crossOriginResourcePolicyCross-Origin-Resource-Policy
permissionsPolicyPermissions-Policy, off unless set
extraAny other headers

Content security policy

The default policy forbids everything, which is right for JSON and wrong for a web page. Pass directives as an object.

securityHeaders({
  contentSecurityPolicy: {
    'default-src': ["'self'"],
    'img-src': ["'self'", 'data:', 'https://cdn.example.com'],
    'script-src': ["'self'"],
    'style-src': ["'self'", "'unsafe-inline'"],
    'frame-ancestors': ["'none'"],
  },
});

Avoid 'unsafe-inline' and 'unsafe-eval' in script-src. They remove most of the protection the policy gives.

HTTPS

Strict-Transport-Security only takes effect over HTTPS, and browsers ignore it on plain HTTP, so it is safe to leave on in development. Turn on preload only once every subdomain serves HTTPS, since it is hard to undo.

What these headers do not do

They harden the browser's handling of your responses. They do not validate input, encode output or authenticate anyone.

On this page