Jotform is one of those services that looks simple from the outside: paste an embed, publish the page, done.
Then CSP blocks it.
Usually the first breakage shows up as:
- the form iframe never loads
- custom scripts from Jotform get refused
- form submissions fail silently
- file uploads or payment widgets break
- the browser console fills with
Refused to frame,Refused to connect, orRefused to load script
If you’re embedding Jotform on a site with a real Content Security Policy, you need to explicitly allow the Jotform origins your embed uses. There isn’t a single universal one-liner that covers every Jotform feature, because the exact domains can vary by embed type, region, and enabled integrations.
I’d start narrow, verify in the browser console, and only then widen the policy.
The main CSP directives that matter for Jotform
For most Jotform embeds, these are the directives you’ll touch:
frame-src: allow the Jotform iframescript-src: allow Jotform JS if you use their script embedstyle-src: sometimes needed for embedded stylingimg-src: logos, icons, uploaded assetsconnect-src: XHR/fetch/websocket calls from the embedform-action: usually stays on your site unless your own page posts elsewhereframe-ancestors: controls who can embed your page, not Jotform’s page
The most common mistake is mixing up frame-src and frame-ancestors.
- Use
frame-srcto allow your page to embed Jotform. - Use
frame-ancestorsto control whether someone else can embed your page.
Minimal CSP for a basic Jotform iframe embed
If you’re using a plain iframe embed, start here:
Content-Security-Policy:
default-src 'self';
frame-src 'self' https://form.jotform.com https://www.jotform.com;
img-src 'self' data: https:;
style-src 'self' 'unsafe-inline';
script-src 'self';
connect-src 'self';
base-uri 'self';
object-src 'none';
form-action 'self';
frame-ancestors 'self';
This works when:
- your page itself does not load Jotform JavaScript
- Jotform is only rendered inside an iframe
- you don’t need extra Jotform assets outside the iframe
A matching HTML snippet:
<iframe
src="https://form.jotform.com/260000000000000"
width="100%"
height="600"
frameborder="0"
title="Contact form">
</iframe>
If the iframe is blocked, the browser console will usually tell you the exact blocked origin. Add that origin to frame-src.
CSP for Jotform’s script embed
Some teams use Jotform’s JavaScript embed instead of a hardcoded iframe. In that case, script-src matters too.
Example policy:
Content-Security-Policy:
default-src 'self';
script-src 'self' https://form.jotform.com https://www.jotform.com;
frame-src 'self' https://form.jotform.com https://www.jotform.com;
style-src 'self' 'unsafe-inline';
img-src 'self' data: https:;
connect-src 'self' https://form.jotform.com https://www.jotform.com;
base-uri 'self';
object-src 'none';
form-action 'self';
frame-ancestors 'self';
Example embed:
<script src="https://form.jotform.com/jsform/260000000000000"></script>
If you already run a nonce-based CSP, don’t casually throw in 'unsafe-inline' for scripts just to make this work. Keep your script policy strict and add only the required Jotform hosts.
A stricter production-friendly baseline
A lot of production policies already look something like the real-world header below:
content-security-policy:
default-src 'self' https://www.googletagmanager.com https://*.cookiebot.com https://*.google-analytics.com;
script-src 'self' 'nonce-N2M4NjhlYzktNTRjYy00N2ZkLTk5NTctNmVlODUzYzRmYWUz' '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'
To add Jotform safely, I’d extend only the directives that need it.
Example: add Jotform to an existing strict policy
Content-Security-Policy:
default-src 'self' https://www.googletagmanager.com https://*.cookiebot.com https://*.google-analytics.com;
script-src 'self' 'nonce-{RANDOM_NONCE}' 'strict-dynamic' https://www.googletagmanager.com https://*.cookiebot.com https://*.google-analytics.com https://form.jotform.com https://www.jotform.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 https://form.jotform.com https://www.jotform.com;
frame-src 'self' https://consentcdn.cookiebot.com https://form.jotform.com https://www.jotform.com;
frame-ancestors 'none';
base-uri 'self';
form-action 'self';
object-src 'none';
A couple of practical notes:
- If you only use an iframe embed, you may not need Jotform in
script-src. - If Jotform assets come from a different subdomain in your setup, add that exact host instead of broad wildcards.
- If you use
'strict-dynamic', remember host allowlists can behave differently depending on whether a nonce/hash-trusted script loads more scripts.
If you want a “just make it work” version
I don’t love broad allowlists, but sometimes you need to get a launch unstuck and tighten later.
Content-Security-Policy:
default-src 'self';
script-src 'self' https://*.jotform.com;
style-src 'self' 'unsafe-inline' https://*.jotform.com;
img-src 'self' data: https:;
connect-src 'self' https://*.jotform.com;
frame-src 'self' https://*.jotform.com;
base-uri 'self';
object-src 'none';
form-action 'self';
frame-ancestors 'self';
This is broader than I’d keep long term, but it’s useful for confirming whether CSP is the actual problem.
Then tighten it based on observed requests.
Nginx example
add_header Content-Security-Policy "
default-src 'self';
script-src 'self' https://form.jotform.com https://www.jotform.com;
style-src 'self' 'unsafe-inline';
img-src 'self' data: https:;
connect-src 'self' https://form.jotform.com https://www.jotform.com;
frame-src 'self' https://form.jotform.com https://www.jotform.com;
base-uri 'self';
object-src 'none';
form-action 'self';
frame-ancestors 'self';
" always;
Apache example
Header always set Content-Security-Policy "default-src 'self'; script-src 'self' https://form.jotform.com https://www.jotform.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; connect-src 'self' https://form.jotform.com https://www.jotform.com; frame-src 'self' https://form.jotform.com https://www.jotform.com; base-uri 'self'; object-src 'none'; form-action 'self'; frame-ancestors 'self';"
Express / Node.js example
app.use((req, res, next) => {
res.setHeader(
"Content-Security-Policy",
[
"default-src 'self'",
"script-src 'self' https://form.jotform.com https://www.jotform.com",
"style-src 'self' 'unsafe-inline'",
"img-src 'self' data: https:",
"connect-src 'self' https://form.jotform.com https://www.jotform.com",
"frame-src 'self' https://form.jotform.com https://www.jotform.com",
"base-uri 'self'",
"object-src 'none'",
"form-action 'self'",
"frame-ancestors 'self'"
].join("; ")
);
next();
});
Report-Only first, always
If this is a live site, use Content-Security-Policy-Report-Only before enforcement.
Content-Security-Policy-Report-Only:
default-src 'self';
script-src 'self' https://form.jotform.com https://www.jotform.com;
frame-src 'self' https://form.jotform.com https://www.jotform.com;
connect-src 'self' https://form.jotform.com https://www.jotform.com;
img-src 'self' data: https:;
style-src 'self' 'unsafe-inline';
base-uri 'self';
object-src 'none';
form-action 'self';
frame-ancestors 'self';
That gives you a safe way to catch missing hosts before users do.
Common Jotform CSP breakages
1. The iframe is blocked
Console error points to frame-src.
Fix:
frame-src 'self' https://form.jotform.com https://www.jotform.com;
2. Script embed does nothing
Console error points to script-src.
Fix:
script-src 'self' https://form.jotform.com https://www.jotform.com;
3. Submission or validation fails
Usually connect-src.
Fix:
connect-src 'self' https://form.jotform.com https://www.jotform.com;
4. Images or branding assets are missing
Usually img-src.
Fix:
img-src 'self' data: https:;
That https: allowance is broad, but for third-party embeds it’s often the least annoying option.
My recommended starting policy
If you want one sane default for most Jotform deployments, use this and adjust from console errors:
Content-Security-Policy:
default-src 'self';
script-src 'self' https://form.jotform.com https://www.jotform.com;
style-src 'self' 'unsafe-inline';
img-src 'self' data: https:;
connect-src 'self' https://form.jotform.com https://www.jotform.com;
frame-src 'self' https://form.jotform.com https://www.jotform.com;
base-uri 'self';
object-src 'none';
form-action 'self';
frame-ancestors 'self';
That’s not the most locked-down policy possible, but it’s clean, understandable, and usually enough to ship.
For stricter patterns and ready-to-use policy variants, see the official CSP docs and the policy examples at https://csp-examples.com.