Skip to content

Configure security headers

The docs path serves an interactive "try it" console, so by default Register wraps both the docs and spec handlers with go/transport's conservative security-header middleware:

  • X-Content-Type-Options: nosniff
  • X-Frame-Options: DENY and Content-Security-Policy: frame-ancestors 'none'
  • Referrer-Policy: no-referrer
  • HSTS off (enable it deliberately for HTTPS deployments)

You get this with no configuration:

openapi.Register(mux, spec) // security headers applied by default

The console renders and issues try-it requests normally under these defaults — they restrict framing and MIME sniffing, not scripting.

Turn HSTS on for an HTTPS deployment

HSTS is off by default because it is only meaningful over TLS, and advertising it from a host that also answers plain HTTP can wedge clients. Enable it deliberately, on a server reachable only over HTTPS:

import (
    "time"

    transporthttp "gitlab.com/phpboyscout/go/transport/http"
)

openapi.Register(mux, spec,
    openapi.WithSecurityHeaderOptions(
        transporthttp.WithHSTS(365*24*time.Hour, true, false),
    ),
)

The arguments are maxAge, includeSubdomains and preload. A non-positive maxAge leaves HSTS disabled.

Customise the other headers

Every go/transport/http SecurityHeadersOption is accepted and forwarded verbatim to transporthttp.SecurityHeadersMiddleware:

Option Default it overrides
WithContentTypeOptions(value string) nosniff
WithFrameOptions(value string) DENY
WithReferrerPolicy(policy string) no-referrer
WithContentSecurityPolicy(policy string) frame-ancestors 'none'
WithHSTS(maxAge, includeSubdomains, preload) (off)

An empty string omits the header entirely for the first three.

Repeated WithSecurityHeaderOptions calls accumulate rather than replace, so options from several places in your wiring all apply, last value winning per header.

Allow the docs page to be framed

X-Frame-Options: DENY and frame-ancestors 'none' block embedding the docs page in an <iframe> — including in your own developer portal. Relax both, or neither; browsers that honour CSP frame-ancestors prefer it to the legacy header:

openapi.Register(mux, spec,
    openapi.WithSecurityHeaderOptions(
        transporthttp.WithFrameOptions("SAMEORIGIN"),
        transporthttp.WithContentSecurityPolicy("frame-ancestors 'self'"),
    ),
)

Careful with a full Content-Security-Policy

WithContentSecurityPolicy replaces the whole header, not just the frame-ancestors directive — you own the complete policy from then on.

The generated docs page carries an inline style="height: 100vh; margin: 0" attribute on <body>, and Stoplight Elements injects styles at runtime, so a policy without 'unsafe-inline' in style-src breaks the page's layout. The default policy sets only frame-ancestors, which is why it does not.

Opt out

If an outer middleware chain already sets equivalent headers, disable the built-in wrapper so headers are not set twice:

openapi.Register(mux, spec, openapi.WithoutSecurityHeaders())

Warning

WithoutSecurityHeaders serves the interactive docs UI without nosniff/frame/referrer protections. Use it only when a surrounding chain provides them; otherwise keep the default.