> For the complete documentation index, see [llms.txt](https://docs.apryse.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.apryse.com/web/get-started/faq/content-security-policy.md).

# Use a Content Security Policy (CSP) with WebViewer

World's #1 PDF SDK Library for Web, Mobile, Server, Desktop

[MDN says](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP/) a Content Security Policy (CSP) is: "an added layer of security that helps to detect and mitigate certain types of attacks, including Cross-Site Scripting (XSS) and data injection attacks."

## Recommended policy

WebViewer requires certain CSP directives to be used. If you do not need to support [embedded JavaScript](/web/forms/embedded-js.md) then the current recommended policy is:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```sh
script-src 'self' 'wasm-unsafe-eval' blob:; font-src 'self' data: blob:; img-src 'self' data: blob:; style-src 'self' 'nonce-<cspNonce>'; connect-src https://www.pdftron.com/ https://pws-collect.pdftron.com/ https://proxy.pdftron.com/
```

{% endcode %}
{% endtab %}
{% endtabs %}

If you use this policy and want to make sure the embedded JavaScript warnings/errors don't show up because of the CSP then you can [disable embedded JavaScript in WebViewer](/web/forms/embedded-js.md#disabling).

### Allowing embedded JavaScript

If you need to enable to embedded JavaScript then you'll currently need to enable `unsafe-eval` for `script-src`.

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```sh
script-src 'self' 'unsafe-inline' 'unsafe-eval' blob:; font-src 'self' data:; img-src 'self' data: blob:; style-src 'self' 'nonce-<cspNonce>'; connect-src https://www.pdftron.com/ https://pws-collect.pdftron.com/ https://proxy.pdftron.com/
```

{% endcode %}
{% endtab %}
{% endtabs %}

### Why is `connect-src https://www.pdftron.com/` necessary?

The `https://www.pdftron.com/` domain hosts fonts that WebViewer will download when necessary for certain documents.

It is possible to self-serve these fonts on another domain by following the [guide for self serving substitute fonts](/web/get-started/faq/self-serve-substitute-fonts.md).

### Why is `connect-src https://pws-collect.pdftron.com/` necessary?

When WebViewer is provided a license key that transmits usage data back to Apryse, then this is needed to ensure that the API server hosted at <https://pws-collect.pdftron.com/> can be reached.

If the license key used is not a usage-based license key, then adding this URL to the `connect-src` directive is not necessary.

### Why is `connect-src https://proxy.pdftron.com/` necessary?

The proxy server is used in cases where WebViewer needs to contact a Certificate Revocation List (CRL) server or Online Certificate Status Protocol (OCSP) server for a subset of [Digital Signature Verification](/web/digital-signature/verify-pdf.md) calls.

This Proxy server can be substituted for any other Proxy server via the API [Core.PDFNet.VerificationOptions.setRevocationProxyPrefix](https://sdk.apryse.com/api/web/Core.PDFNet.VerificationOptions.html#setRevocationProxyPrefix).

Otherwise, if your instance of WebViewer is not utilizing [Digital Signature Verification](/web/digital-signature/verify-pdf.md), then this can be removed.

### Why is `script-src 'wasm-unsafe-eval'` necessary?

WebViewer leverages WebAssembly for several native modules, and therefore requires a CSP directive that allows WebAssembly to execute.

While the term "unsafe" is used, we are currently unaware of any security risks related to the `wasm-unsafe-eval` directive, as it is distinct from the more dangerous `unsafe-eval` directive. For avoidance of doubt, `wasm-unsafe-eval` does not allow for the JavaScript `eval` method to be invoked, whereas `unsafe-eval` does allow for the `eval` method to be invoked.

Furthermore, as of this writing, the official WebAssembly documentation [recommends this directive](https://github.com/WebAssembly/content-security-policy/blob/4c61db828b4a0739e4500e8d42d0ec85ef05505a/proposals/CSP.md#the-wasm-unsafe-eval-source-directive) when WebAssembly compilation and instantiation is required.

### Why is `style-src 'nonce-'` necessary?

WebViewer UI injects a small number of inline `<style>` elements at runtime (for theming, dynamic styles or layout, etc.). If your CSP blocks inline styles and you do not provide a nonce, those styles may be rejected by the browser and parts of the UI will render incorrectly.

To enable these styles, you can either:

* Use a nonce which is recommended. Find more information in our [Nonces](#Nonces) guide.
* Add `'unsafe-inline'` to your CSP header.

### What if the web server I want to deploy WebViewer on does not allow the `script-src blob:` directive?

There is a WebViewer constructor option [`disableObjectURLBlobs`](https://sdk.apryse.com/api/web/global.html#WebViewer__anchor) that will remove the need for `script-src blob:`, that can be called like so:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```sh
WebViewer({
  // ...
  disableObjectURLBlobs: true,
  // ...
}, document.getElementById('viewer')).then(instance => {
  // ...
});
```

{% endcode %}
{% endtab %}
{% endtabs %}

For additional context, WebViewer by default loads various compressed JavaScript worker files that are compressed as the G-Zip (`.gz`) or Brotli (`.br`) compression types. In order for WebViewer to uncompress these file extensions, they need to be opened with the browser's [URL.createObjectURL](https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL/) method, which returns a [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob/) that is then loaded as a `blob:` URL.

If the CSP directive `script-src blob:` is not available, then this operation is not permitted.

Please note that the downside of this is that WebViewer then needs to load uncompressed versions of these worker files, which may have a minor impact on performance, depending on the client's network speed, though it is worth noting that the size difference between the compressed and uncompressed assets are not major.

### What if the web server I want to deploy WebViewer on does not allow the `script-src 'wasm-unsafe-eval'` directive?

WebViewer can operate in a non-WebAssembly context with [`disableObjectURLBlobs`](https://sdk.apryse.com/api/web/global.html#WebViewer__anchor), like so:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```sh
WebViewer({
  // ...
  disableObjectURLBlobs: true,
  backendType: WebViewer.BackendTypes.ASM,
  // ...
}, document.getElementById('viewer')).then(instance => {
  // ...
});
```

{% endcode %}
{% endtab %}
{% endtabs %}

However, this does require uncompressing the non-WASM PDF & Office worker files manually, as Apryse does not ship the uncompressed versions of these files due to the file size of the uncompressed files.

#### The files to uncompress

**PDF Workers**

WebViewer has a `fullAPI` option as part of the constructor, which would look like this extending from the code example above:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```sh
WebViewer({
  // ...
  disableObjectURLBlobs: true,
  backendType: WebViewer.BackendTypes.ASM,
  fullAPI: true,
  // ...
}, document.getElementById('viewer')).then(instance => {
  // ...
});
```

{% endcode %}
{% endtab %}
{% endtabs %}

If `fullAPI: true` is being used, then the following commands should be run in `lib/core/pdf/full`, otherwise if `fullAPI` is not being used, perform the commands in `lib/core/pdf/lean`.

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```sh
# Copy and rename the new JavaScript worker file to end with .gz
cp PDFNetC.gz.js.mem PDFNetC.js.gz
# Uncompress the newly copied G-Zip compressed file
gzip -d PDFNetC.js.gz
# Copy and rename the Binary worker file to end with .gz and move it up one level, as this is where PDFNetC.js expects to load the uncompressed binary mem worker
cp PDFNetC.gz.mem ../PDFNetC.js.mem.gz
# Uncompress the newly copied G-Zip compressed file
gzip -d ../PDFNetC.js.mem.gz
```

{% endcode %}
{% endtab %}
{% endtabs %}

**Office Workers**

For modern Office files (`.docx`, `.xlsx` and `.pptx`), perform the following commands in `lib/core/office`:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```sh
# Copy and rename the new JavaScript worker file to end with .gz
cp WebOfficeWorker.gz.js.mem WebOfficeWorker.js.gz
# Uncompress the newly copied G-Zip compressed file
gzip -d WebOfficeWorker.js.gz
# Copy and rename the Binary worker file to end with .gz
cp WebOfficeWorker.gz.mem WebOfficeWorker.js.mem.gz
# Uncompress the newly copied G-Zip compressed file
gzip -d WebOfficeWorker.js.mem.gz
```

{% endcode %}
{% endtab %}
{% endtabs %}

Please note that the instructions for the PDF workers also need to be completed for the Office workers to operate correctly.

**Legacy Office Workers**

For legacy Office files (`.doc`, `.xls` and `.ppt`), perform the following commands in `lib/core/legacyOffice`:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```sh
# Copy and rename the new JavaScript worker file to end with .gz
cp WebB2XOfficeWorker.gz.js.mem WebB2XOfficeWorker.js.gz
# Uncompress the newly copied G-Zip compressed file
gzip -d WebB2XOfficeWorker.js.gz
# Copy and rename the Binary worker file to end with .gz
cp WebB2XOfficeWorker.gz.mem WebB2XOfficeWorker.js.mem.gz
# Uncompress the newly copied G-Zip compressed file
gzip -d WebB2XOfficeWorker.js.mem.gz
```

{% endcode %}
{% endtab %}
{% endtabs %}

Please note that the instructions for the PDF workers also need to be completed for the Office workers to operate correctly.

### Do we support trusted type headers?

We only support trusted types in WebViewer Core, and not with the UI yet. After adding the trusted types header to the content security policy just like how it is described in this [MDN article](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy/require-trusted-types-for#syntax), we have to specify which policies we trust. Other than the policy used in your page for other scripts and elements, you will need to include the `webviewer` and `webviewer-po` policies. This will allow WebViewer to continue to work with sanitized trusted types.

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
// Adding header in Express.js
app.use(express.static(path.resolve(__dirname), {
  setHeaders: (res) => {
    const csp = "require-trusted-types-for 'script'; trusted-types webviewer webviewer-po;";
    res.set('Content-Security-Policy', csp);
    res.set('Service-Worker-Allowed', '/');
  },
}));
```

{% endcode %}
{% endtab %}
{% endtabs %}

### Nonces

**What is a nonce?**

A nonce (number used once) is a random, cryptographic value used to validate fresh data. The server generates a fresh nonce on every page load and includes it in the page's `Content-Security-Policy` (CSP) header. The same value is then attached as a `nonce` attribute on any inline `<script>` or `<style>` tag that the page is allowed to run.

The browser only executes inline scripts/styles whose `nonce` attribute matches the one in the CSP header. Anything injected later by an attacker (e.g., via XSS) will be blocked.

A good nonce is:

* **Unique per request:** A new nonce is generated between page loads.
* **Random: G**enerated with a cryptographic RNG on the server.
* **Unpredictable:** Not generated using user input.

An example CSP header and matching tag:

{% tabs %}
{% tab title="HTML" %}
{% code lineNumbers="true" %}

```html
Content-Security-Policy: style-src 'self' 'nonce-abc123'
```

{% endcode %}
{% endtab %}

{% tab title="HTML" %}
{% code lineNumbers="true" %}

```html
<style nonce="abc123">
  /* allowed by CSP because the nonce matches */
</style>
```

{% endcode %}
{% endtab %}
{% endtabs %}

**When is a nonce needed?**

A nonce is needed when your CSP does not allow `unsafe-inline` for `script-src` or `style-src`, but your application still needs to load some inline scripts or styles.

For WebViewer, a nonce enables the viewer to safely inject and verify a small number of inline `<style>` elements at runtime. These elements are used for dynamic theming, styling, and layouts.

**How to pass a nonce to WebViewer**

WebViewer now accepts a `cspNonce` option in its constructor. Pass the same value that you put in the `Content-Security-Policy` header for the page, e.g.:

{% tabs %}
{% tab title="HTML" %}
{% code lineNumbers="true" %}

```html
Content-Security-Policy: style-src 'self' 'nonce-abc123'
```

{% endcode %}
{% endtab %}

{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
WebViewer({
  path: '/lib',
  cspNonce: 'abc123',
}, document.getElementById('viewer'));
```

{% endcode %}
{% endtab %}
{% endtabs %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.apryse.com/web/get-started/faq/content-security-policy.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
