Berserk Docs

CSP Nonce

Taking the UI's Content-Security-Policy nonce from a front proxy instead of self-minting it

The Berserk UI ships a strict Content-Security-Policy on every page. Inline <script> and <style> elements are gated on a nonce — a single-use token that appears both in the policy header and on each element the policy permits.

By default the UI mints that nonce itself, and no configuration is needed. If you front Berserk with a proxy that generates the nonce and injects it into the CSP policy header — a common requirement in environments that mandate a platform-wide CSP — point the UI at the request header your proxy uses and it will adopt that value instead.

Two modes

The mode is selected by a single setting, ui.config.cspNonceHeader (CSP_NONCE_HEADER). There is no default header name: the name is whatever your proxy sends.

cspNonceHeaderBehavior
"" (default)Self-mint. The UI generates a fresh nonce per request. Nothing else to configure.
a header nameProxy-supplied. The UI reads the nonce from that request header on every browser request and uses its value.

Proxy-supplied nonce

values.yaml
ui:
  config:
    cspNonceHeader: X-CSP-Nonce

The request flow is then:

  1. Your proxy generates a nonce per request and forwards it to Berserk on the configured request header.
  2. The UI reads it and injects it into every <script nonce="..."> and <style nonce="..."> element it renders, and into the Content-Security-Policy response header it emits.
  3. Your proxy injects the same value into the CSP policy header it emits.

The header name is matched case-insensitively, so X-CSP-Nonce and x-csp-nonce are equivalent.

Accepted values

A nonce must be a base64 token: 1–256 characters from A-Z, a-z, 0-9, +, /, _, =, - (standard and URL-safe base64, which also covers hex). This is enforced so a stray header value cannot break out of the policy header or a nonce="" attribute.

Proxy-supplied mode is fail-closed.

Once cspNonceHeader is set, a browser request arriving without a valid nonce on that header is rejected with a 500 — the UI never falls back to self-minting, because silently minting its own nonce would produce a page whose elements don't match the policy your proxy emitted. In practice this means the UI is reachable only through the proxy.

Two paths are unaffected by design: Kubernetes health probes (/health/*) and static assets, neither of which renders a nonced page.

Header name collisions

If you also use trusted-proxy authentication, the nonce header must not reuse the name configured for gateway.config.trustedProxy.userHeader or the on-behalf-of header. Those two are deliberately stripped at the gateway so an asserted identity can never reach a downstream service unverified — a nonce sent on one of those names would be dropped before the UI sees it, and every request would fail closed. Any other header name passes through the gateway untouched.

Composing with your proxy's policy

When your proxy emits its own Content-Security-Policy, the browser receives two policy headers and enforces both — the effective policy is their intersection. The nonce can be entirely correct and the UI still lose functionality if the proxy's policy omits a directive the UI depends on.

The simplest arrangement is to let the proxy's policy stay permissive for the Berserk origin and let the UI's own policy — already strict — do the restricting. If your proxy's policy has to be authoritative, it must permit at least the following:

DirectiveWhy the UI needs it
script-src 'wasm-unsafe-eval'The query editor's KQL language server is WebAssembly. Without it the editor loses highlighting, completion, diagnostics, and hover.
worker-src 'self'That language server runs in a web worker.
connect-src 'self' ws: wss:The UI is a LiveView application driven over a WebSocket. Without it the UI degrades to long-polling at best.
style-src-attr 'unsafe-inline'Inline style= attributes carry computed values (e.g. a layout width). A nonce cannot apply to an attribute.
img-src *User avatars are served by arbitrary identity providers.

Note that the UI nonces <style> elements (style-src-elem) but not style attributes (style-src-attr), which no nonce can cover. A proxy policy that collapses the two into a single style-src will behave differently from the UI's own.

A blocked resource reports a CSP violation in the browser console naming the directive that rejected it — start there when a feature misbehaves only behind the proxy.

On this page