Campaign Monitor forms are simple until CSP gets involved.

Then you add an embedded signup form, hit refresh, and the browser starts yelling about blocked scripts, frames, styles, or form submissions. I’ve had this happen enough times that I prefer starting with a known-good policy and tightening from there instead of guessing.

This guide is the practical version: what to allow, what to avoid, and copy-paste CSP examples you can actually use.

What Campaign Monitor forms usually need

Campaign Monitor form embeds typically use some combination of:

  • a hosted form page on a createsend.com domain
  • inline styles or embedded styles
  • JavaScript loaded from Campaign Monitor infrastructure
  • a form POST target on a Campaign Monitor endpoint
  • sometimes an iframe embed instead of raw HTML

Your CSP needs to account for the exact embed type you use.

The three directives that usually matter most are:

  • script-src
  • frame-src
  • form-action

And depending on the embed, you may also need:

  • style-src
  • img-src
  • connect-src

Start by identifying the embed type

Campaign Monitor forms are commonly added in one of these ways:

1. Raw HTML form embedded in your page

You paste Campaign Monitor’s form markup into your page. The form submits directly to a Campaign Monitor URL.

This mostly affects:

  • form-action
  • style-src
  • img-src

2. JavaScript-powered embed

You include a script that renders or enhances the form.

This affects:

  • script-src
  • connect-src
  • possibly style-src

3. Iframe embed

You load the form from Campaign Monitor in an iframe.

This affects:

  • frame-src

If you’re not sure which one you have, inspect the page in DevTools and look for:

  • <script src="...createsend...">
  • <iframe src="...createsend...">
  • <form action="...createsend...">

Minimal CSP additions for Campaign Monitor

If your site already has a CSP, these are the directives you’ll usually update.

For embedded form submissions

Content-Security-Policy: form-action 'self' https://*.createsend.com;

If the form posts to a specific Campaign Monitor host, use that exact host instead of a wildcard.

Example:

Content-Security-Policy: form-action 'self' https://xyz.createsend.com;

I prefer exact hosts whenever possible. Wildcards are convenient, but they age badly.

For iframe embeds

Content-Security-Policy: frame-src 'self' https://*.createsend.com;

Again, tighter is better:

Content-Security-Policy: frame-src 'self' https://abc.createsend.com;

For Campaign Monitor scripts

If your form embed loads JavaScript from Campaign Monitor:

Content-Security-Policy: script-src 'self' https://*.createsend.com;

If your current CSP is strict and nonce-based, don’t blindly weaken it with 'unsafe-inline'. Add the external host first and only loosen the policy if you confirm the embed actually requires inline script.

For styles

A lot of third-party form embeds sneak in inline styles. If Campaign Monitor’s embed uses them, you may need:

Content-Security-Policy: style-src 'self' 'unsafe-inline' https://*.createsend.com;

I don’t love 'unsafe-inline', but for marketing forms it’s common. If you can host and sanitize the form markup yourself, do that instead.

Copy-paste CSP policies

Here are the versions I’d actually start with.

1. HTML form only

Use this when you pasted the form HTML directly into your page and it submits to Campaign Monitor.

Content-Security-Policy:
  default-src 'self';
  form-action 'self' https://*.createsend.com;
  style-src 'self' 'unsafe-inline';
  img-src 'self' data: https:;
  base-uri 'self';
  object-src 'none';

Why this works:

  • form-action allows the signup POST
  • style-src covers inline form styling
  • img-src handles logos, tracking pixels, or hosted assets

2. JavaScript embed

Use this if Campaign Monitor injects or enhances the form with JS.

Content-Security-Policy:
  default-src 'self';
  script-src 'self' https://*.createsend.com;
  connect-src 'self' https://*.createsend.com;
  form-action 'self' https://*.createsend.com;
  style-src 'self' 'unsafe-inline' https://*.createsend.com;
  img-src 'self' data: https:;
  base-uri 'self';
  object-src 'none';

If the script dynamically loads more scripts, you may need to inspect the network panel and add those hosts too.

3. Iframe embed

Use this when the form is rendered inside an iframe.

Content-Security-Policy:
  default-src 'self';
  frame-src 'self' https://*.createsend.com;
  style-src 'self' 'unsafe-inline';
  img-src 'self' data: https:;
  base-uri 'self';
  object-src 'none';

If your page also includes JS from Campaign Monitor, combine this with the previous policy.

A stricter version for mature setups

If your site already runs a tighter CSP, keep it that way and add only what Campaign Monitor needs.

Here’s the style I prefer:

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

A good real-world pattern is keeping a narrow default-src, then explicitly allowing the few third-party origins each feature needs. The CSP on HeaderTest follows that model:

content-security-policy: default-src 'self' https://www.googletagmanager.com https://*.cookiebot.com https://*.google-analytics.com; script-src 'self' 'nonce-YTQ0ZmMxNWEtYzU0MS00NThlLWExYjAtM2MyYjYzMDUyNzg1' '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 the mindset you want: specific directives, specific hosts, minimal guessing.

Nginx example

add_header Content-Security-Policy "default-src 'self'; script-src 'self' https://*.createsend.com; connect-src 'self' https://*.createsend.com; form-action 'self' https://*.createsend.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; base-uri 'self'; object-src 'none';" always;

Apache example

Header always set Content-Security-Policy "default-src 'self'; script-src 'self' https://*.createsend.com; connect-src 'self' https://*.createsend.com; form-action 'self' https://*.createsend.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; base-uri 'self'; object-src 'none';"

Express / Node.js example

app.use((req, res, next) => {
  res.setHeader(
    "Content-Security-Policy",
    "default-src 'self'; " +
      "script-src 'self' https://*.createsend.com; " +
      "connect-src 'self' https://*.createsend.com; " +
      "form-action 'self' https://*.createsend.com; " +
      "style-src 'self' 'unsafe-inline'; " +
      "img-src 'self' data: https:; " +
      "base-uri 'self'; " +
      "object-src 'none';"
  );
  next();
});

Common CSP errors with Campaign Monitor

“Refused to send form data to…”

That’s almost always form-action.

Fix:

form-action 'self' https://*.createsend.com;

“Refused to frame … because it violates frame-src”

Your iframe embed is blocked.

Fix:

frame-src 'self' https://*.createsend.com;

“Refused to load the script…”

Your embed script host is missing from script-src.

Fix:

script-src 'self' https://*.createsend.com;

“Refused to apply inline style…”

The form markup or embed injects inline CSS.

Fix:

style-src 'self' 'unsafe-inline';

If you can avoid this by moving styles to a stylesheet you control, do it. Marketing embeds rarely make that easy.

Use Report-Only first if you can

If you’re adding Campaign Monitor to a production site with an existing CSP, start with report-only mode.

Content-Security-Policy-Report-Only:
  default-src 'self';
  script-src 'self' https://*.createsend.com;
  connect-src 'self' https://*.createsend.com;
  form-action 'self' https://*.createsend.com;
  frame-src 'self' https://*.createsend.com;
  style-src 'self' 'unsafe-inline';
  img-src 'self' data: https:;
  base-uri 'self';
  object-src 'none';

That gives you a clean way to see what the form actually needs before you enforce anything.

My recommendation

For Campaign Monitor forms, I’d do this:

  1. Identify whether you’re using HTML, JS, or iframe embed
  2. Allow only the exact Campaign Monitor host if possible
  3. Add form-action first
  4. Add frame-src or script-src only if the embed requires them
  5. Keep object-src 'none' and base-uri 'self'
  6. Use report-only before rollout

If you want more ready-made policy patterns for third-party services, csp-examples.com is a good shortcut.

The big mistake is treating CSP like a giant allowlist bucket and dumping https: everywhere. That gets the page working, but it defeats the point. Campaign Monitor forms don’t need a sloppy policy. They just need a precise one.