security-headers
Injects pre-computed security response headers. Each header is
individually configurable or disableable (false). All values are built
at factory time for zero per-request overhead.
Pipeline stage: Cross-cutting
Import
Section titled “Import”import { securityHeaders } from "@centralping/ergo";Options
Section titled “Options”| Option | Type | Default | Description |
|---|---|---|---|
contentSecurityPolicy | string | false | "default-src 'none'" | Content-Security-Policy |
strictTransportSecurity | string | object | false | false | HSTS — off by default (see note) |
xContentTypeOptions | string | boolean | 'nosniff' | X-Content-Type-Options (true → 'nosniff', false disables) |
xFrameOptions | string | false | 'DENY' | X-Frame-Options |
referrerPolicy | string | false | 'no-referrer' | Referrer-Policy |
xXssProtection | string | false | '0' | X-XSS-Protection (0 disables legacy browser filter) |
permissionsPolicy | string | — | Permissions-Policy (omitted by default) |
HSTS Note
Section titled “HSTS Note”HSTS defaults to false because this middleware returns pre-computed
header tuples with no access to the request object, so it cannot verify
HTTPS. Per RFC 6797 §7.2, HSTS MUST only be sent over secure transport.
ergo-router’s transport-level middleware performs the HTTPS check and
enables HSTS appropriately.
Structured HSTS
Section titled “Structured HSTS”When using a string: 'max-age=31536000; includeSubDomains; preload'
When using an object: { maxAge: 31536000, includeSubDomains: true, preload: true }
Return Value
Section titled “Return Value”Always returns response headers:
{ response: { headers: [ ["Content-Security-Policy", "default-src 'none'"], ["X-Content-Type-Options", "nosniff"], ["X-Frame-Options", "DENY"], ["Referrer-Policy", "no-referrer"], ["X-XSS-Protection", "0"] ] }}Error Responses
Section titled “Error Responses”None.
import { compose, securityHeaders } from "@centralping/ergo";
const pipeline = compose( securityHeaders(),);
// Custom configurationconst pipeline = compose( securityHeaders({ xFrameOptions: "SAMEORIGIN", permissionsPolicy: "camera=(), microphone=()", xXssProtection: false, }),);const router = createRouter({ defaults: { securityHeaders: { contentSecurityPolicy: "default-src 'self'", xFrameOptions: "SAMEORIGIN", }, },});CSP Implications
Section titled “CSP Implications”The default contentSecurityPolicy is "default-src 'none'" — the most
restrictive Content Security Policy possible. This is appropriate for
JSON API endpoints that never serve HTML content, but it will break
any response that contains inline styles, scripts, images, or other
embedded resources.
When default-src 'none' Matters
Section titled “When default-src 'none' Matters”If your endpoint returns JSON (the common case for ergo APIs), the restrictive CSP is invisible — browsers do not evaluate CSP for JSON responses. The default exists as defense-in-depth: if an attacker somehow causes your API to return HTML, the CSP prevents the browser from executing any embedded content.
Relaxing CSP for HTML-Serving Routes
Section titled “Relaxing CSP for HTML-Serving Routes”If you have routes that serve HTML content (e.g., server-rendered pages,
health check dashboards, or documentation endpoints), override
contentSecurityPolicy on those routes:
import { compose, securityHeaders } from "@centralping/ergo";
const pipeline = compose( securityHeaders({ contentSecurityPolicy: "default-src 'self'; style-src 'self' 'unsafe-inline'", }),);// Per-route overriderouter.get("/dashboard", { securityHeaders: { contentSecurityPolicy: "default-src 'self'; style-src 'self' 'unsafe-inline'", }, execute: (req, res, acc) => ({ response: { body: "<html>...</html>" }, }),});Transport-Level CSP
Section titled “Transport-Level CSP”ergo-router’s transport layer applies security headers to every
response — including 404, 405, and 429 short-circuits that bypass the
route pipeline. By default, transport-level CSP is off (undefined),
so only matched routes receive a CSP header via the route-level
middleware.
To set a CSP policy at the transport level:
const router = createRouter({ transport: { security: { csp: "default-src 'self'", }, },});To disable CSP entirely for a route:
securityHeaders({ contentSecurityPolicy: false })RFC References
Section titled “RFC References”API Reference
Section titled “API Reference”See the auto-generated securityHeaders API docs.