Paperform is easy to embed and easy to break with a bad CSP.

If you run a locked-down site and drop in a Paperform embed, you’ll usually hit one of these:

  • the iframe refuses to load
  • form assets get blocked
  • custom scripts around the embed stop working
  • submissions fail because connect-src is too strict
  • your own site can’t frame Paperform because frame-src is missing

This guide is the practical version: what to allow, when to allow it, and copy-paste policies you can start with.

The short version

If you embed a Paperform form in an iframe, your CSP usually needs at least:

Content-Security-Policy:
  default-src 'self';
  frame-src https://paperform.co https://*.paperform.co;
  child-src https://paperform.co https://*.paperform.co;
  script-src 'self';
  style-src 'self';
  img-src 'self' data: https:;
  connect-src 'self';
  frame-ancestors 'self';
  base-uri 'self';
  form-action 'self';
  object-src 'none';

That’s the baseline for your site embedding Paperform.

If you use Paperform on a custom domain, or load Paperform assets from another Paperform-controlled host, you’ll need to expand the policy. I’ll show that in a minute.

First: know what CSP controls here

When Paperform is embedded, there are two separate CSP concerns:

  1. Your page’s CSP

    • Controls whether your page is allowed to load the Paperform iframe.
    • Controls any wrapper scripts, styles, analytics, and postMessage helpers on your page.
  2. Paperform’s own CSP

    • Controls what happens inside the Paperform document.
    • You usually do not control this unless you fully proxy or host something custom.

That distinction matters. If your page blocks frame-src, the Paperform iframe never gets a chance to render.

Most common Paperform embedding pattern

A typical embed looks something like this:

<div data-paperform-id="my-form" data-height="600"></div>
<script src="https://paperform.co/__embed.min.js"></script>

Or an iframe embed:

<iframe
  src="https://paperform.co/form/my-form"
  width="100%"
  height="600"
  frameborder="0"
  allowfullscreen>
</iframe>

These two patterns may need different directives:

  • script-src for the Paperform embed script
  • frame-src for the iframe
  • connect-src if your page-side code talks to APIs
  • img-src and style-src if the embed helper injects assets

CSP for iframe-based Paperform embeds

If you only use a plain iframe, start here:

Content-Security-Policy:
  default-src 'self';
  frame-src 'self' https://paperform.co https://*.paperform.co;
  child-src 'self' https://paperform.co https://*.paperform.co;
  script-src 'self';
  style-src 'self' 'unsafe-inline';
  img-src 'self' data: https:;
  connect-src 'self';
  font-src 'self' https: data:;
  frame-ancestors 'self';
  base-uri 'self';
  form-action 'self';
  object-src 'none';

Why this works

  • frame-src allows loading the Paperform iframe
  • child-src is there for older browser behavior and belt-and-suspenders compatibility
  • img-src https: avoids random broken icons and remote images
  • style-src 'unsafe-inline' is sometimes necessary if your own frontend injects inline styles around embeds

If your site doesn’t use inline styles, remove 'unsafe-inline'.

CSP for the Paperform embed script

If you load Paperform’s JavaScript embed helper, allow the script origin too:

Content-Security-Policy:
  default-src 'self';
  script-src 'self' https://paperform.co https://*.paperform.co;
  frame-src 'self' https://paperform.co https://*.paperform.co;
  child-src 'self' https://paperform.co https://*.paperform.co;
  style-src 'self' 'unsafe-inline';
  img-src 'self' data: https:;
  connect-src 'self' https://paperform.co https://*.paperform.co;
  font-src 'self' https: data:;
  frame-ancestors 'self';
  base-uri 'self';
  form-action 'self';
  object-src 'none';

I’d only add connect-src for Paperform if your browser console shows blocked XHR or fetch requests. Don’t cargo-cult extra hosts into a policy unless you actually need them.

If you use a custom Paperform domain

A lot of teams use branded form URLs like:

  • https://forms.example.com
  • https://apply.example.com

When that happens, your CSP must allow your custom Paperform host, not just paperform.co.

Example:

Content-Security-Policy:
  default-src 'self';
  script-src 'self' https://forms.example.com;
  frame-src 'self' https://forms.example.com;
  child-src 'self' https://forms.example.com;
  style-src 'self' 'unsafe-inline';
  img-src 'self' data: https:;
  connect-src 'self' https://forms.example.com;
  font-src 'self' https: data:;
  frame-ancestors 'self';
  base-uri 'self';
  form-action 'self';
  object-src 'none';

If the embed script still comes from paperform.co but the form itself loads from forms.example.com, allow both:

Content-Security-Policy:
  default-src 'self';
  script-src 'self' https://paperform.co https://forms.example.com;
  frame-src 'self' https://paperform.co https://forms.example.com;
  child-src 'self' https://paperform.co https://forms.example.com;
  style-src 'self' 'unsafe-inline';
  img-src 'self' data: https:;
  connect-src 'self' https://paperform.co https://forms.example.com;
  font-src 'self' https: data:;
  frame-ancestors 'self';
  base-uri 'self';
  form-action 'self';
  object-src 'none';

A stricter modern policy

If your app already uses nonces and you want a tighter CSP, this is a better shape:

Content-Security-Policy:
  default-src 'self';
  script-src 'self' 'nonce-rAnd0m123' 'strict-dynamic' https://paperform.co;
  frame-src 'self' https://paperform.co https://*.paperform.co;
  style-src 'self';
  img-src 'self' data: https:;
  connect-src 'self' https://paperform.co https://*.paperform.co;
  font-src 'self';
  frame-ancestors 'self';
  base-uri 'self';
  form-action 'self';
  object-src 'none';
  report-to default-endpoint;
  report-uri https://csp-report.example.com/report;

This only makes sense if your app is already built around nonces. If not, don’t fake it. A broken nonce rollout is worse than a simple allowlist.

For more ready-made patterns, csp-examples.com is handy.

Nginx example

add_header Content-Security-Policy "
  default-src 'self';
  script-src 'self' https://paperform.co https://*.paperform.co;
  frame-src 'self' https://paperform.co https://*.paperform.co;
  child-src 'self' https://paperform.co https://*.paperform.co;
  style-src 'self' 'unsafe-inline';
  img-src 'self' data: https:;
  connect-src 'self' https://paperform.co https://*.paperform.co;
  font-src 'self' https: data:;
  frame-ancestors 'self';
  base-uri 'self';
  form-action 'self';
  object-src 'none';
" always;

Apache example

Header always set Content-Security-Policy "default-src 'self'; script-src 'self' https://paperform.co https://*.paperform.co; frame-src 'self' https://paperform.co https://*.paperform.co; child-src 'self' https://paperform.co https://*.paperform.co; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; connect-src 'self' https://paperform.co https://*.paperform.co; font-src 'self' https: data:; frame-ancestors 'self'; base-uri 'self'; form-action 'self'; object-src 'none';"

Express / Node example

app.use((req, res, next) => {
  res.setHeader(
    "Content-Security-Policy",
    [
      "default-src 'self'",
      "script-src 'self' https://paperform.co https://*.paperform.co",
      "frame-src 'self' https://paperform.co https://*.paperform.co",
      "child-src 'self' https://paperform.co https://*.paperform.co",
      "style-src 'self' 'unsafe-inline'",
      "img-src 'self' data: https:",
      "connect-src 'self' https://paperform.co https://*.paperform.co",
      "font-src 'self' https: data:",
      "frame-ancestors 'self'",
      "base-uri 'self'",
      "form-action 'self'",
      "object-src 'none'"
    ].join("; ")
  );
  next();
});

Debugging blocked Paperform embeds

Open DevTools and look for messages like:

  • Refused to frame ... because it violates the following Content Security Policy directive: "frame-src ..."
  • Refused to load the script ... because it violates "script-src ..."
  • Refused to connect to ... because it violates "connect-src ..."

That error tells you exactly which directive is wrong.

I usually test in this order:

  1. Add only frame-src
  2. If using embed JS, add script-src
  3. If network calls fail, add connect-src
  4. Tighten everything else after it works

That’s faster than pasting a giant permissive policy and pretending it’s secure.

If you want to inspect your live header and catch obvious mistakes, headertest.com is useful for a quick sanity check. A real-world CSP from the site looks like this:

content-security-policy:
default-src 'self' https://www.googletagmanager.com https://*.cookiebot.com https://*.google-analytics.com;
script-src 'self' 'nonce-MTE2N2MzNzQtOGFhMy00ZGYzLTgxYTgtM2Q3ZmNlNGMxMGJl' 'strict-dynamic' https://www.googletagmanager.com https://*.cookiebot.com https://*.google-analytics.com;
style-src 'self' 'unsafe-inline' https://www.googletagmanager.com https://*.cookiebot.com https://consent.cookiebot.com;
img-src 'self' data: https:;
font-src 'self';
connect-src 'self' https://api.headertest.com https://tallycdn.com https://or.headertest.com wss://or.headertest.com https://*.google-analytics.com https://*.googletagmanager.com https://*.cookiebot.com;
frame-src 'self' https://consentcdn.cookiebot.com;
frame-ancestors 'none';
base-uri 'self';
form-action 'self';
object-src 'none'

That’s a good example of a CSP that grew around actual dependencies instead of wishful thinking.

Report-Only first if you can

For production sites, I prefer rolling out Paperform CSP changes with Content-Security-Policy-Report-Only first:

Content-Security-Policy-Report-Only:
  default-src 'self';
  script-src 'self' https://paperform.co https://*.paperform.co;
  frame-src 'self' https://paperform.co https://*.paperform.co;
  connect-src 'self' https://paperform.co https://*.paperform.co;
  report-uri https://csp-report.example.com/report;

That gives you violation reports without breaking the form immediately.

Common mistakes

Using default-src and assuming it covers everything

It’s the fallback, not magic. If you define script-src, frame-src, or connect-src, those specific directives take over.

Forgetting frame-src

This is the classic one for Paperform iframe embeds.

Allowing https: everywhere

It works, but it’s lazy. Fine for img-src sometimes. Bad idea for script-src.

Leaving object-src unset

Set it to 'none'. There’s almost never a reason not to.

Confusing frame-ancestors with frame-src

  • frame-src = what your page can load
  • frame-ancestors = who can embed your page

Different problem, different directive.

Safe starting policies

1. Plain iframe embed

Content-Security-Policy:
  default-src 'self';
  frame-src 'self' https://paperform.co https://*.paperform.co;
  child-src 'self' https://paperform.co https://*.paperform.co;
  img-src 'self' data: https:;
  style-src 'self';
  script-src 'self';
  connect-src 'self';
  font-src 'self';
  base-uri 'self';
  form-action 'self';
  frame-ancestors 'self';
  object-src 'none';

2. Embed script + iframe

Content-Security-Policy:
  default-src 'self';
  script-src 'self' https://paperform.co https://*.paperform.co;
  frame-src 'self' https://paperform.co https://*.paperform.co;
  child-src 'self' https://paperform.co https://*.paperform.co;
  style-src 'self' 'unsafe-inline';
  img-src 'self' data: https:;
  connect-src 'self' https://paperform.co https://*.paperform.co;
  font-src 'self' https: data:;
  base-uri 'self';
  form-action 'self';
  frame-ancestors 'self';
  object-src 'none';

3. Custom Paperform domain

Content-Security-Policy:
  default-src 'self';
  script-src 'self' https://forms.example.com;
  frame-src 'self' https://forms.example.com;
  child-src 'self' https://forms.example.com;
  style-src 'self' 'unsafe-inline';
  img-src 'self' data: https:;
  connect-src 'self' https://forms.example.com;
  font-src 'self' https: data:;
  base-uri 'self';
  form-action 'self';
  frame-ancestors 'self';
  object-src 'none';

If you remember one thing, make it this: start narrow, watch the console, and only add the exact Paperform origins your integration actually uses. That’s how CSP stays useful instead of turning into decorative security.