Skip to content

Security Headers

The SecurityHeadersManager applies security headers to every response. It ships with 10 hardened defaults and supports CSP, HSTS, and custom header configuration.

These 10 headers are applied to every response when securityHeaders.enabled is true (the default):

HeaderDefault Value
X-Content-Type-Optionsnosniff
X-Frame-OptionsSAMEORIGIN
X-XSS-Protection1; mode=block
Referrer-Policystrict-origin-when-cross-origin
Permissions-Policygeolocation=(), microphone=(), camera=()
X-Permitted-Cross-Domain-Policiesnone
X-Download-Optionsnoopen
Cross-Origin-Embedder-Policyrequire-corp
Cross-Origin-Opener-Policysame-origin
Cross-Origin-Resource-Policysame-origin
const config: SecurityConfig = {
securityHeaders: null,
};

Or disable while keeping the object:

const config: SecurityConfig = {
securityHeaders: {
enabled: false,
},
};

Strict-Transport-Security is generated from the hsts sub-object:

const config: SecurityConfig = {
securityHeaders: {
enabled: true,
hsts: {
maxAge: 31536000,
includeSubdomains: true,
preload: true,
},
},
};

This produces: Strict-Transport-Security: max-age=31536000; includeSubDomains; preload

Content-Security-Policy is built from a directive map:

const config: SecurityConfig = {
securityHeaders: {
enabled: true,
csp: {
'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'],
},
},
};

This produces: Content-Security-Policy: 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

Override individual headers through the securityHeaders object:

const config: SecurityConfig = {
securityHeaders: {
enabled: true,
frameOptions: 'DENY',
referrerPolicy: 'no-referrer',
permissionsPolicy: 'camera=(), microphone=(), geolocation=(self)',
},
};

Add arbitrary headers:

const config: SecurityConfig = {
securityHeaders: {
enabled: true,
custom: {
'X-Custom-Header': 'my-value',
'X-Request-ID-Required': 'true',
},
},
};

All header values are validated:

  • CR (\r) and LF (\n) characters are rejected (prevents HTTP response splitting)
  • Values exceeding 8192 characters are rejected
  • Control characters (\x00-\x08, \x0b, \x0c, \x0e-\x1f) are stripped

When enableCors is true, the SecurityHeadersManager also manages CORS headers on error responses (403, 429, etc.). This ensures browsers can read error details from blocked cross-origin requests.

CORS headers applied to error responses:

HeaderValue
Access-Control-Allow-OriginMatching origin or *
Access-Control-Allow-MethodsConfigured methods
Access-Control-Allow-HeadersConfigured headers
Access-Control-Allow-Credentialstrue (if enabled)

Headers are cached in-memory per request path with a 5-minute TTL and a max cache size of 1000 entries. When Redis is enabled, CSP, HSTS, and custom header configurations are persisted to Redis with a 24-hour TTL for shared access across processes.