EmbedPDF

Security & CSP

The viewer runs under a strict Content Security Policy: no 'unsafe-inline', no 'unsafe-eval', and Trusted Types on. This is the policy to start from:

Content-Security-Policy:
  default-src 'self';
  script-src 'self' 'wasm-unsafe-eval';
  style-src 'self';
  worker-src 'self';
  img-src 'self' blob: data:;
  connect-src 'self';
  require-trusted-types-for 'script';
  trusted-types embedpdf;
DirectiveWhy the viewer needs it
script-src 'wasm-unsafe-eval'The engine is WebAssembly, and the browser asks for this to compile any WebAssembly. It allows nothing else: no eval, no inline scripts. With CloudPDF it’s needed only for Stamps and Signatures, which open their libraries in the browser.
worker-src 'self'The engine runs in a Web Worker, loaded from a file next to the viewer’s
img-src blob: data:Pages are drawn into images the browser makes, and tool cursors are small inline images
connect-src 'self'The engine’s WebAssembly, your documents, and any fonts or stamps you configure. Add each origin you load from.
trusted-types embedpdfThe viewer’s one Trusted Types policy, which only allows URLs of its own files and the ones you configure

style-src 'self' is enough, without 'unsafe-inline' or a nonce: the viewer never adds a <style> tag or a style attribute to your page. Its styles go straight into its own shadow DOM, which the browser doesn’t count as inline styles.

Loading from a CDN#

A browser only starts a Web Worker from its own origin. When the viewer’s script comes from jsDelivr, it starts the engine’s workers from blob: URLs instead. Add the CDN to script-src and connect-src (the script, its chunks and the WebAssembly come from there), and allow blob: workers:

script-src 'self' https://cdn.jsdelivr.net 'wasm-unsafe-eval';
connect-src 'self' https://cdn.jsdelivr.net;
worker-src blob:;

To keep worker-src 'self', serve the viewer’s files from your own site: install it from npm, or copy its folder. See Self-hosting.

Builds that use blob: workers#

Most bundlers (Vite, webpack, Rspack, Parcel, Next.js) emit the workers as files of your build, so worker-src 'self' covers them. Two builds can’t, and start the workers from blob: URLs, so they need worker-src blob::

  • Angular, which loads the engine’s portable build;
  • plain esbuild, which needs that build too: see Self-hosting.

To keep worker-src 'self' there, copy the two worker files to your site and point the viewer at them: see Self-hosting.

Your bundler’s Trusted Types policy#

With require-trusted-types-for 'script', the browser also checks how your bundler loads the rest of your app’s code. Vite, esbuild and Angular need nothing more. The others load it through a Trusted Types policy of their own, which trusted-types names next to embedpdf:

BuildWhat to add
webpack 5output: { trustedTypes: 'webpack' } in your config, and trusted-types embedpdf webpack;
Rspackoutput: { trustedTypes: 'rspack' } in your config, and trusted-types embedpdf rspack;
Next.js (webpack)trusted-types embedpdf nextjs#bundler;. Next.js’s own inline scripts need its nonce setup.
Next.js (Turbopack)Turbopack can’t run with require-trusted-types-for 'script' yet: leave that line out.

A PDF you can’t trust#

A PDF can carry links, scripts and forms that send data somewhere. The viewer treats every one as untrusted:

  • Scripts in a PDF, for form checks and calculations, run in a sandbox with no access to your page, its cookies or the network. Turn them off with plugins: { actions: { javascript: { enabled: false } } }.
  • Links open only after the user clicks them, and only http, https, mailto and tel links. javascript: and file: links do nothing, and so does a link relative to the document, such as /account, which would otherwise open a page of your own app.
  • Form submissions never send anything by themselves. A form’s submit button tells your code, and you decide where the data goes. See Actions & scripts.

Good to know#

Annotations and comments#

Text people type into annotations and comments is shown as text, never as HTML, so a comment can’t run a script in anyone else’s viewer.

Signatures#

A signature is checked against the certificates you trust and nothing else: the viewer never fetches a certificate or a revocation list from a URL inside the PDF.

Was this page helpful?

Your feedback goes directly to the documentation team.