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.
cspNonceHeader | Behavior |
|---|---|
"" (default) | Self-mint. The UI generates a fresh nonce per request. Nothing else to configure. |
| a header name | Proxy-supplied. The UI reads the nonce from that request header on every browser request and uses its value. |
Proxy-supplied nonce
ui:
config:
cspNonceHeader: X-CSP-NonceThe request flow is then:
- Your proxy generates a nonce per request and forwards it to Berserk on the configured request header.
- The UI reads it and injects it into every
<script nonce="...">and<style nonce="...">element it renders, and into theContent-Security-Policyresponse header it emits. - 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:
| Directive | Why 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.