Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 45 additions & 1 deletion docs/platforms/javascript/guides/react-router/manual-setup.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -272,9 +272,53 @@ Exporting `instrumentations` uses React Router's [instrumentation API](https://r
</SplitSection>
</SplitLayout>

<Expandable title="Does your app use Content Security Policy nonces?">

If your Node.js app uses a nonce-based Content Security Policy (CSP), pass `getNonce` to `createSentryHandleRequest`. This lets you use your app's per-request nonce without replacing Sentry's request handler or trace meta-tag injection.

Sentry calls `getNonce({ request, loadContext })` once for each rendered request and passes the returned nonce to both `<ServerRouter>` and `renderToPipeableStream`. The callback must return a string or `undefined` synchronously. If you omit it or return `undefined`, the helper doesn't supply a nonce.

For example, if your app uses React Router's `RouterContextProvider`, define a shared context key:

```ts {filename: app/context.ts}
import { createContext } from "react-router";

export const cspNonceContext = createContext<string>();
```

In your server middleware, generate a fresh, unpredictable nonce for each response, such as with Node.js's `randomBytes(16).toString("base64")`. Store it with `context.set(cspNonceContext, nonce)`, then read it in your server entry:

```tsx {filename: app/entry.server.tsx}
import * as Sentry from "@sentry/react-router";
import { createReadableStreamFromReadable } from "@react-router/node";
import { renderToPipeableStream } from "react-dom/server";
import { RouterContextProvider, ServerRouter } from "react-router";
import { cspNonceContext } from "./context";

const handleRequest = Sentry.createSentryHandleRequest({
ServerRouter,
renderToPipeableStream,
createReadableStreamFromReadable,
getNonce: ({ loadContext }) =>
loadContext instanceof RouterContextProvider
? loadContext.get(cspNonceContext)
: undefined,
});

export default handleRequest;

// Keep your existing handleError and instrumentations exports.
```

Without React Router middleware, `loadContext` is your app's `AppLoadContext` instead. Read the nonce from the property where your server stores it.

Your app must also include the **same nonce** in the response's `Content-Security-Policy` header, for example as `'nonce-${nonce}'` in `script-src`. Sentry doesn't generate the nonce or set the CSP header. Don't reuse a nonce across responses or generate a second nonce for the header. See [React Router's CSP guidance](https://reactrouter.com/how-to/security) for details.

</Expandable>

<Expandable title="Do you need to customize your handleRequest function?">

If you need to customize the logic of your `handleRequest` function, you'll need to use Sentry's helper functions (`getMetaTagTransformer` and `wrapSentryHandleRequest`) manually:
For CSP nonces, use the `getNonce` option described above. If you need other custom rendering logic in your `handleRequest` function, use Sentry's helper functions (`getMetaTagTransformer` and `wrapSentryHandleRequest`) manually:

```tsx {1-4, 44-45, 69-70}
import {
Expand Down
Loading