React Router
React Router - and Remix, which it grew out of - renders inline scripts of its own, so a strict policy needs a nonce reaching each of them. The framework handles the propagation for you, with one exception that catches people out when streaming.
What React Router gives you
- A
nonceprop on<Scripts>,<ScrollRestoration>and<LiveReload>, passed straight through to the rendered<script>tags. - A
nonceprop on<ServerRouter>, which becomes the default for nonce-aware components including<Links>,<Scripts>and<ScrollRestoration>. It is proxied through React context, so you set it once. - Per-component overrides: a
nonceon an individual component wins over the<ServerRouter>value.
Setting the header
Generate the nonce per request, set it on the header, and pass the same value into <ServerRouter>. Your URL comes from the Setup page:
const nonce = crypto.randomBytes(16).toString('base64')
responseHeaders.set(
'Content-Security-Policy-Report-Only',
`default-src 'self'; script-src 'self' 'nonce-${nonce}' 'strict-dynamic'; ` +
`report-uri https://abc123.report-uri.com/r/d/csp/reportOnly`,
)
Things to watch for
- Streaming needs the nonce in three places, not one: the
Content-Security-Policyheader, the nonce-aware components, and therenderToPipeableStreamcall inentry.server.ts. Miss the third and the shell renders while the streamed chunks get blocked - which looks like a hydration bug rather than a policy problem. <ScrollRestoration>must be rendered immediately before<Scripts>. That is a framework requirement independent of CSP, but getting it wrong while also introducing a nonce makes the cause much harder to see.- Setting the nonce on
<ServerRouter>covers the framework's components, not third-party scripts in your root route. Those need their own policy entries or their own nonce attributes.
Official documentation
Start Monitoring with Report URI
Already using us for CSP? Find your report-uri value on Setup, or view your CSP reports.
New to Report URI? Create an account and grab your report-uri value from the Setup page.