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

# Content Security Policy (CSP)

The Content-Security-Policy HTTP response header helps you reduce XSS risks on modern browsers by declaring which dynamic resources are allowed to load.

If you are making use of CSP, add the Gleap sources to the following directives. This is the complete allowlist — merge each line into the matching directive of your existing policy (`'self'` stands in for whatever your policy already allows):

```text theme={null}
script-src  'self' 'unsafe-inline' https://*.gleap.io https://*.gleap.ai;
style-src   'self' 'unsafe-inline' https://*.gleap.io https://*.gleap.ai;
font-src    'self' https://*.gleap.io https://*.gleap.ai data:;
connect-src 'self' https://*.gleap.io https://*.gleap.ai wss://*.gleap.io wss://*.gleap.ai;
frame-src   'self' https://*.gleap.io https://*.gleap.ai;
media-src   'self' https://*.gleap.io https://*.gleap.ai;
img-src     'self' https://*.gleap.io https://*.gleap.ai data: blob:;
```

<Info>
  `*.gleap.io` covers the EU [data region](/documentation/guides/data-regions) and the global hosts that every region uses (SDK, messenger app, static assets). `*.gleap.ai` covers the US data region (`api.us.gleap.ai`, `ws.us.gleap.ai`, `sockets.us.gleap.ai`, …); a CSP host wildcard matches subdomains at any depth. We recommend allowing both, regardless of your region. US projects always need both.
</Info>

<Warning>
  Bundling the SDK yourself (via npm) does **not** shrink this list. Gleap is effectively two apps: the SDK inside your bundle, and the messenger app the SDK loads at runtime from `messenger-app.gleap.io`. Even with no `<script src="...gleap...">` tag on your page, the messenger's script and stylesheet still load from a Gleap origin and are checked against your `script-src` and `style-src`.
</Warning>

#### Why each directive is needed

The messenger loads in a chain, and each step is checked against a *different* directive — allowing one step does not allow the next:

1. **`connect-src`** — the SDK `fetch()`es the messenger document from `https://messenger-app.gleap.io`. Having the host in `frame-src` is not enough: a fetch of an iframe's host is still a fetch. (API calls to `api.gleap.io`, or `api.us.gleap.ai` in the US region, need this too.)
2. **`script-src` / `style-src`** — the messenger's `<script>` and `<link rel="stylesheet">` load from `messenger-app.gleap.io` into a frame that inherits your page's policy.
3. **`connect-src` (`wss:`)** — the SDK opens `wss://ws.gleap.io` and the messenger opens `wss://sockets.gleap.io` (US region: `wss://ws.us.gleap.ai` and `wss://sockets.us.gleap.ai`). The `https://` wildcards do **not** cover these: schemes are matched separately, so `wss://` sources must be listed on their own.
4. **`frame-src`, `font-src`, `img-src`, `media-src`** — the widget frame itself, plus fonts, images and media (e.g. attachments, avatars, voice messages) served from Gleap origins.

If any single step is blocked, the launcher simply does not appear — check the browser console for `Content Security Policy` errors and add the reported origin to the directive named in the error.

<Note>
  `script-src` needs `'unsafe-inline'` **or** a nonce (see [Nonce-based and `strict-dynamic` policies](#nonce-based-and-strict-dynamic-policies) below). The SDK loads the messenger into a frame that inherits your page's policy, and it writes one small inline script into that frame to set the messenger's route. Without `'unsafe-inline'` or a nonce that inline script is blocked, and features that open on a specific route (banners, modals, direct links to a conversation) can render the wrong view. If you would rather not allow `'unsafe-inline'` at all, use a nonce.
</Note>

<Info>
  We strongly recommend using the `https://*.gleap.io`, `https://*.gleap.ai`, `wss://*.gleap.io` and `wss://*.gleap.ai` wildcards. The SDK contacts several Gleap subdomains (`api.gleap.io`, `ws.gleap.io`, `sockets.gleap.io`, `messenger-app.gleap.io`, `outboundmedia.gleap.io`, `app.gleap.io`, `js.gleap.io`; in the US region `api.us.gleap.ai`, `ws.us.gleap.ai` and `sockets.us.gleap.ai` take the place of the first three), and the list may grow as we ship new features. Note that the `https://` wildcards do **not** cover WebSocket connections — `wss://` origins must be allowed separately.
</Info>

### WebSockets in `connect-src`

Gleap uses two WebSocket endpoints, and both must be allowed:

* `wss://ws.gleap.io` (US region: `wss://ws.us.gleap.ai`) — session and event streaming used by the JavaScript SDK.
* `wss://sockets.gleap.io` (US region: `wss://sockets.us.gleap.ai`) — realtime delivery of conversations and notifications in the messenger.

If you'd rather not list them individually, allow `wss://*.gleap.io` and `wss://*.gleap.ai`.

<Note>
  Gleap previously relied on [Pusher](https://pusher.com) for realtime messaging, which required `*.pusher.com` entries in `connect-src`. Realtime traffic now runs entirely on Gleap infrastructure (`wss://sockets.gleap.io`, or `wss://sockets.us.gleap.ai` in the US region), so any `https://*.pusher.com` or `wss://*.pusher.com` entries you added for Gleap can be removed.
</Note>

### Nonce-based and `strict-dynamic` policies

If your policy is nonce-based, the SDK can carry your nonce on everything it creates, including the messenger bundle and the inline route script described above. This requires SDK **16.3.4 or newer**.

The simplest setup is to put your nonce on the SDK's own `<script>` tag. The SDK reads it from there automatically, and no further configuration is needed:

```html theme={null}
<script nonce="YOUR_NONCE" src="https://sdk.gleap.io/latest/index.js"></script>
```

If your loader cannot put the nonce on that tag (some tag managers and bundlers cannot), pass it explicitly **before** calling `Gleap.initialize()`:

```javascript theme={null}
Gleap.setCSPNonce("YOUR_NONCE");
Gleap.initialize("YOUR_API_KEY");
```

With a nonce in place you no longer need `'unsafe-inline'` in `script-src`, and you do not need to allowlist a content hash for the inline script.

<Warning>
  If your policy uses `'strict-dynamic'`, a nonce is **required**. `'strict-dynamic'` tells the browser to ignore host-based sources entirely, so `https://*.gleap.io` and `https://*.gleap.ai` in `script-src` / `script-src-elem` have no effect and the messenger will not load without one. Hash-pinning the messenger bundle is not a workaround: its filename carries a build hash that changes with every release, and a hash source for an external script is only honored when the tag also carries a matching `integrity` attribute.
</Warning>

Note that `'strict-dynamic'` only applies to script directives. `frame-src`, `connect-src`, `img-src`, `font-src` and `media-src` still need the origins listed above.

### Cross-origin isolation (COEP / COOP)

Gleap also works on pages that opt into [cross-origin isolation](https://web.dev/articles/coop-coep) with `Cross-Origin-Embedder-Policy` (COEP), in both `require-corp` and `credentialless` mode, together with `Cross-Origin-Opener-Policy: same-origin`. No SDK setting is needed.

Under COEP the browser only lets a page embed cross-origin resources that either pass a CORS check or announce themselves with `Cross-Origin-Resource-Policy: cross-origin`. Gleap covers both cases: every API call the SDK and the messenger make is a CORS request, and every Gleap origin that the widget loads without CORS sends `Cross-Origin-Resource-Policy: cross-origin`. These are the global hosts `sdk.gleap.io`, `messenger-app.gleap.io`, `outboundmedia.gleap.io` and `static.gleap.io`, plus the hosts of your [data region](/documentation/guides/data-regions): `api.gleap.io`, `files.gleap.io` and `staticfiles.gleap.io` in the EU region, `api.us.gleap.ai`, `files.us.gleap.ai` and `staticfiles.us.gleap.ai` in the US region.

<Note>
  Keep `https://messenger-app.gleap.io` (or the `https://*.gleap.io` wildcard) in `connect-src`. The SDK fetches the messenger document and writes it into a frame that is same-origin with your page, so the messenger runs under your page's COEP rather than as a cross-origin `<iframe>`. If that fetch is blocked by CSP the SDK falls back to a direct `<iframe src>` load, which a cross-origin-isolated page refuses because the messenger deliberately does not opt into COEP itself (it embeds third-party content such as videos in help articles). This requires SDK **16.0.10 or newer**.
</Note>

<Info>
  With `require-corp`, images that the messenger loads from hosts outside Gleap (for example a profile picture hosted by a third-party identity provider, or an image in a help article that is hosted elsewhere) only display if that host sends `Cross-Origin-Resource-Policy` too. `credentialless` does not have this restriction and is the recommended mode when your threat model allows it.
</Info>

### Strict CSPs without wildcards

If your security policy forbids wildcards, here is the full explicit allowlist the SDK uses today:

```javascript theme={null}
// All regions
https://sdk.gleap.io
https://js.gleap.io
https://app.gleap.io
https://app.gleap.ai
https://messenger-app.gleap.io
https://outboundmedia.gleap.io

// EU region (default)
https://api.gleap.io
wss://ws.gleap.io
wss://sockets.gleap.io

// US region
https://api.us.gleap.ai
wss://ws.us.gleap.ai
wss://sockets.us.gleap.ai
```

<Warning>
  This explicit list can change without notice as we add features or migrate infrastructure. The `https://*.gleap.io`, `https://*.gleap.ai`, `wss://*.gleap.io` and `wss://*.gleap.ai` wildcards are the safest choice.
</Warning>

Depending on your setup you might need to do some further customizations. Please check the browser console for any CSP errors and add the reported origins to the matching directive.
