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.comdomain - 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-srcframe-srcform-action
And depending on the embed, you may also need:
style-srcimg-srcconnect-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-actionstyle-srcimg-src
2. JavaScript-powered embed
You include a script that renders or enhances the form.
This affects:
script-srcconnect-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-actionallows the signup POSTstyle-srccovers inline form stylingimg-srchandles 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:
- Identify whether you’re using HTML, JS, or iframe embed
- Allow only the exact Campaign Monitor host if possible
- Add
form-actionfirst - Add
frame-srcorscript-srconly if the embed requires them - Keep
object-src 'none'andbase-uri 'self' - 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.