Skip to content

Security Headers (CSP & HSTS)

pidgn ships two small middleware for the two security response headers almost every HTML app benefits from. Both build their header values at comptime so the runtime path is a single header append.

OptionTypeDefaultDescription
policy[]const u8"default-src 'self'"Full policy string.
report_onlyboolfalseEmit Content-Security-Policy-Report-Only instead (browsers report but don’t block).
pidgn.csp(.{
.policy = "default-src 'self'; img-src 'self' data:; script-src 'self' 'unsafe-inline'",
}),

The middleware appends the header on every response. To add a nonce per request, set it yourself from the handler before rendering a template.

When introducing CSP to an existing app, start in report-only mode so violations get reported to you without breaking pages:

pidgn.csp(.{
.policy = "default-src 'self'; report-uri /csp-report",
.report_only = true,
}),

When your telemetry is clean, flip report_only back to false.

OptionTypeDefaultDescription
max_ageu6431_536_000 (1 year)How long browsers will remember to use HTTPS.
include_subdomainsbooltrueWhether the policy applies to all subdomains.
preloadboolfalseSubmits the site for the HSTS preload list — only enable once your entire domain tree is HTTPS-only.
pidgn.hsts(.{
.max_age = 63_072_000, // 2 years
.include_subdomains = true,
.preload = true,
}),

The emitted header for the above config is:

Strict-Transport-Security: max-age=63072000; includeSubDomains; preload

Built entirely at comptime — no per-request formatting.

const App = pidgn.Router.define(.{
.middleware = &.{
pidgn.errorHandler(.{}),
pidgn.logger,
pidgn.csp(.{ .policy = "default-src 'self'; img-src 'self' data:" }),
pidgn.hsts(.{}),
pidgn.session(.{}),
pidgn.csrf(.{}),
// ... your other middleware
},
.routes = routes,
});
  • HSTS requires HTTPS. Only enable HSTS on a domain you serve exclusively over HTTPS — a browser that has seen the header will refuse to make plain HTTP requests to it. Breaking this on a live domain is painful.
  • Preload is a one-way ticket. Removal from the HSTS preload list takes months. Enable preload = true only after you’re sure.
  • CSP + inline scripts. If you serve inline <script> blocks, you need either 'unsafe-inline' (defeats most of CSP) or a nonce-based policy. The nonce pattern needs framework work — open a PR if you want first-class support.