> ## Documentation Index
> Fetch the complete documentation index at: https://docs.dynamic.xyz/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Content Security Policy (CSP)

> Configure a strict Content Security Policy with Dynamic's SDK using nonce-based inline styles.

## Overview

A [Content Security Policy (CSP)](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) is a security standard that helps prevent cross-site scripting (XSS), clickjacking, and other code injection attacks. It works by telling the browser exactly which sources of content are trusted.

Dynamic's SDK injects inline `<style>` elements to render its authentication widgets, including styles within Shadow DOM containers. By default, a strict CSP that disallows inline styles (`style-src 'self'`) will block these styles and break the widget's appearance.

The `cspNonce` setting solves this by letting you pass a server-generated nonce to the SDK. Dynamic applies that nonce to every inline `<style>` and `<link>` tag it creates, so they pass your CSP without requiring `'unsafe-inline'`.

## How it works

1. Your server generates a unique, unpredictable nonce for each page request
2. Your server includes the nonce in both the CSP header and passes it to your application
3. You pass the nonce to `DynamicContextProvider` via `settings.cspNonce`
4. The SDK automatically applies the nonce to all inline styles it injects, including:
   * Shadow DOM reset and widget styles
   * Theme styles (`<style>` and `<link>` tags in `<head>`)
   * CSS overrides
   * Legacy browser compatibility styles

The browser then allows `<style>` tags with the matching nonce and blocks everything else, keeping your strict CSP intact.

## Setup

<Steps>
  <Step title="Generate a nonce on your server">
    Create a unique, unpredictable nonce for every page request. Use a cryptographically secure random value — **never hardcode or reuse a nonce**.

    <Accordion title="Node.js / Express">
      ```js theme={"system"}
      import crypto from 'crypto';

      app.use((req, res, next) => {
        // Generate a unique nonce per request
        res.locals.cspNonce = crypto.randomBytes(16).toString('base64');
        next();
      });
      ```
    </Accordion>

    <Accordion title="Next.js (Middleware)">
      ```ts middleware.ts theme={"system"}
      import { NextResponse } from 'next/server';
      import type { NextRequest } from 'next/server';

      export function middleware(request: NextRequest) {
        const nonce = Buffer.from(crypto.randomUUID()).toString('base64');

        const cspHeader = `
          default-src 'self';
          script-src 'self' 'nonce-${nonce}';
          style-src 'self' 'nonce-${nonce}';
          style-src-elem 'self' 'nonce-${nonce}' https:;
          img-src 'self' data: blob:;
          font-src 'self' data:;
          connect-src 'self' https:;
          frame-src 'self' https://app.dynamicauth.com;
        `.replace(/\n/g, '');

        const response = NextResponse.next();

        response.headers.set('Content-Security-Policy', cspHeader);
        response.headers.set('x-nonce', nonce);

        return response;
      }
      ```

      Then read the nonce in your layout:

      ```tsx app/layout.tsx theme={"system"}
      import { headers } from 'next/headers';

      export default async function RootLayout({ children }: { children: React.ReactNode }) {
        const nonce = (await headers()).get('x-nonce') ?? '';

        return (
          <html lang="en">
            <body>
              <DynamicContextProvider
                settings={{
                  environmentId: 'YOUR_ENVIRONMENT_ID',
                  cspNonce: nonce,
                }}
              >
                {children}
              </DynamicContextProvider>
            </body>
          </html>
        );
      }
      ```
    </Accordion>
  </Step>

  <Step title="Set the CSP header">
    Include the nonce in your `style-src` or `style-src-elem` directive. If you use embedded wallets, also include `frame-src` for the iframe connection.

    <Accordion title="Express.js">
      ```js theme={"system"}
      app.use((req, res, next) => {
        const nonce = res.locals.cspNonce;

        res.setHeader(
          'Content-Security-Policy',
          [
            "default-src 'self'",
            `style-src 'self' 'nonce-${nonce}'`,
            `style-src-elem 'self' 'nonce-${nonce}' https:`,
            `script-src 'self' 'nonce-${nonce}'`,
            "img-src 'self' data: blob:",
            "font-src 'self' data:",
            "connect-src 'self' https:",
            "frame-src 'self' https://app.dynamicauth.com",
          ].join('; ')
        );

        next();
      });
      ```
    </Accordion>

    <Accordion title="HTML meta tag (static sites)">
      For static sites where you cannot set HTTP headers, you can use a meta tag. Note that this is less secure than a server-set header because the nonce is visible in the HTML source.

      ```html theme={"system"}
      <meta
        http-equiv="Content-Security-Policy"
        content="style-src 'self' 'nonce-YOUR_NONCE'; style-src-elem 'self' 'nonce-YOUR_NONCE' https:; frame-src 'self' https://app.dynamicauth.com;"
      />
      ```
    </Accordion>

    <Accordion title="NGINX">
      ```nginx theme={"system"}
      # Generate nonce via a module like nginx-nonce or set_by_lua
      set $csp_nonce $request_id;

      add_header Content-Security-Policy
        "default-src 'self'; style-src 'self' 'nonce-$csp_nonce'; style-src-elem 'self' 'nonce-$csp_nonce' https:; frame-src 'self' https://app.dynamicauth.com;";
      ```
    </Accordion>

    <Accordion title="Vercel (vercel.json)">
      Vercel does not support dynamic nonces in `vercel.json` headers. Use Next.js middleware (shown above) or a Vercel Edge Function to generate nonces per request.
    </Accordion>

    <Warning>
      If you're already setting CSP headers, append the nonce to your existing `style-src` and `style-src-elem` directives rather than replacing them.
    </Warning>
  </Step>

  <Step title="Pass the nonce to DynamicContextProvider">
    Pass the nonce string as `cspNonce` in your provider settings. The SDK will apply it to every inline `<style>` and `<link>` element it creates.

    ```jsx theme={"system"}
    import { DynamicContextProvider } from '@dynamic-labs/sdk-react-core';

    function App() {
      return (
        <DynamicContextProvider
          settings={{
            environmentId: 'YOUR_ENVIRONMENT_ID',
            cspNonce: nonce, // The nonce from your server
          }}
        >
          <YourApp />
        </DynamicContextProvider>
      );
    }
    ```
  </Step>
</Steps>

## Full example: Express + React

Here is a complete example showing nonce generation, CSP header configuration, and SDK integration.

**Server (Express):**

```js server.js theme={"system"}
import express from 'express';
import crypto from 'crypto';
import path from 'path';

const app = express();

app.use((req, res, next) => {
  const nonce = crypto.randomBytes(16).toString('base64');
  res.locals.cspNonce = nonce;

  res.setHeader(
    'Content-Security-Policy',
    [
      "default-src 'self'",
      `style-src 'self' 'nonce-${nonce}'`,
      `style-src-elem 'self' 'nonce-${nonce}' https:`,
      `script-src 'self' 'nonce-${nonce}'`,
      "img-src 'self' data: blob:",
      "font-src 'self' data:",
      "connect-src 'self' https:",
      "frame-src 'self' https://app.dynamicauth.com",
    ].join('; ')
  );

  next();
});

app.get('/', (req, res) => {
  const nonce = res.locals.cspNonce;

  res.send(`
    <!DOCTYPE html>
    <html>
      <head>
        <meta charset="utf-8" />
      </head>
      <body>
        <div id="root"></div>
        <script nonce="${nonce}">
          window.__CSP_NONCE__ = "${nonce}";
        </script>
        <script nonce="${nonce}" src="/bundle.js"></script>
      </body>
    </html>
  `);
});

app.listen(3000);
```

**Client (React):**

```jsx App.tsx theme={"system"}
import { DynamicContextProvider } from '@dynamic-labs/sdk-react-core';
import { EthereumWalletConnectors } from '@dynamic-labs/ethereum';

const cspNonce = (window as any).__CSP_NONCE__;

export const App = () => (
  <DynamicContextProvider
    settings={{
      environmentId: 'YOUR_ENVIRONMENT_ID',
      walletConnectors: [EthereumWalletConnectors],
      cspNonce,
    }}
  >
    <YourApp />
  </DynamicContextProvider>
);
```

## CSP directives reference

Here is a recommended baseline CSP for applications using Dynamic:

| Directive        | Recommended Value                    | Purpose                                                                                |
| ---------------- | ------------------------------------ | -------------------------------------------------------------------------------------- |
| `default-src`    | `'self'`                             | Fallback for all resource types                                                        |
| `style-src`      | `'self' 'nonce-{nonce}'`             | Allows nonced inline styles and same-origin stylesheets                                |
| `style-src-elem` | `'self' 'nonce-{nonce}' https:`      | Allows `<style>` and `<link>` elements with the nonce, plus external HTTPS stylesheets |
| `script-src`     | `'self' 'nonce-{nonce}'`             | Allows nonced inline scripts and same-origin scripts                                   |
| `img-src`        | `'self' data: blob:`                 | Allows images from same origin, data URIs, and blob URIs                               |
| `font-src`       | `'self' data:`                       | Allows fonts from same origin and data URIs                                            |
| `connect-src`    | `'self' https:`                      | Allows API calls to same origin and HTTPS endpoints                                    |
| `frame-src`      | `'self' https://app.dynamicauth.com` | Required for embedded wallets (iframe connection)                                      |

<Info>
  The `style-src-elem` directive (CSP Level 3) provides granular control over `<style>` elements separately from inline `style=""` attributes. If your browser support requires it, use `style-src` instead — it covers both. When a nonce is present in `style-src`, browsers that support CSP Level 2+ will ignore `'unsafe-inline'` for `<style>` elements, requiring the nonce.
</Info>

## Approaches to avoid

| Approach                    | Why to avoid                                                                                  |
| --------------------------- | --------------------------------------------------------------------------------------------- |
| `style-src 'unsafe-inline'` | Allows **all** inline styles, defeating the purpose of CSP for style injection attacks        |
| `style-src 'unsafe-hashes'` | Only works for static inline styles — breaks when Dynamic injects styles with dynamic content |
| No CSP at all               | Leaves your application vulnerable to XSS-based style injection                               |

## Verifying your setup

You can verify that the nonce is correctly applied by inspecting Dynamic's style elements in your browser's DevTools:

1. Open **DevTools > Elements**
2. Find a `.dynamic-shadow-dom` element and expand its shadow root
3. Inspect the `<style>` tags inside — each should have a `nonce` attribute matching your CSP

You can also check the **Console** for CSP violation errors. If the nonce is correctly configured, you should see **no `style-src-elem` violations** from Dynamic's styles.

## Troubleshooting

<Accordion title="Styles are broken but no CSP violations in console">
  Make sure the nonce value passed to `cspNonce` exactly matches the nonce in your CSP header. The CSP header format is `'nonce-{value}'` while the `cspNonce` prop expects just the value (without the `nonce-` prefix or quotes).
</Accordion>

<Accordion title="CSP violations from third-party libraries">
  Other libraries in your application (wallet connectors, toast libraries, etc.) may also inject inline styles without nonce support. These violations are not caused by Dynamic. Check if those libraries offer their own nonce or CSP configuration options.
</Accordion>

<Accordion title="Nonce not applied in development (Vite / webpack-dev-server)">
  Dev servers like Vite inject CSS via inline `<style>` tags for hot module replacement (HMR). These won't have your nonce and will trigger CSP violations. This is expected in development — in production builds, CSS is bundled into external files that load via `<link>` tags (allowed by `'self'`).
</Accordion>

<Accordion title="Embedded wallet iframe blocked">
  If you're using embedded wallets, make sure your CSP includes `frame-src 'self' https://app.dynamicauth.com`. See the [embedded wallet setup guide](/react/wallets/embedded-wallets/mpc/setup) for details.
</Accordion>

## Related resources

* [Security Overview](/overview/security/overview)
* [Mitigating Attack Vectors](/overview/security/threatvectors)
* [Security Best Practices](/overview/security/recommendedpaths)
* [Embedded Wallets CSP Setup](/react/wallets/embedded-wallets/mpc/setup)
* [DynamicContextProvider Reference](/react/reference/providers/dynamiccontextprovider)
* [MDN: Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP)
