Wistia embeds are one of those cases where a clean Content Security Policy gets messy fast.
You start with a tight policy, add one video, and suddenly you’re dealing with frame-src, script-src, img-src, connect-src, media delivery, analytics, and a player that wants to talk to a handful of subdomains. If your site already runs a strict CSP, Wistia can feel like the one integration that pressures you into punching holes everywhere.
I’ve had to do this dance enough times that I think the useful comparison is not “how do I make Wistia work,” but “which CSP approach is the least bad for my setup?”
The two main CSP approaches for Wistia
For most developer teams, there are really two options:
- Practical allowlist CSP
Add the Wistia domains you need toscript-src,frame-src,img-src,connect-src, and sometimesmedia-src. - Strict nonce-based CSP with minimal host exceptions
Keep a nonce-basedscript-src, avoid broad host-based script loading where possible, and isolate Wistia allowances to only the directives it actually needs.
Both can work. Neither is perfect.
A real-world baseline: what many sites already look like
Here’s a real CSP header from headertest.com:
content-security-policy: default-src 'self' https://www.googletagmanager.com https://*.cookiebot.com https://*.google-analytics.com; script-src 'self' 'nonce-YmNiMGE3NDgtYTc2Yi00MzAyLTg4NTYtZGQyOGU2ZTlkODBi' '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 decent modern policy. It already uses:
nonce-...strict-dynamicobject-src 'none'base-uri 'self'frame-ancestors 'none'
That’s the good news.
The bad news: this policy will block a standard Wistia embed unless you add explicit allowances.
What Wistia usually needs
Exact domains can vary a bit depending on embed type and delivery path, but a typical Wistia embed often needs some combination of:
fast.wistia.comfast.wistia.netembedwistia-a.akamaihd.net
And depending on player behavior and assets:
- scripts from Wistia-hosted domains
- iframe embedding
- image thumbnails/posters
- media streaming
- XHR/fetch/beacon/websocket-style connections
You should verify the final set in your own browser devtools and CSP reports. Don’t blindly cargo-cult a giant allowlist from random blog posts. That’s how CSP turns into decorative security.
For directive behavior details, the official reference is the MDN CSP docs, and if you want a cleaner directive-by-directive breakdown, https://csp-guide.com is handy.
Option 1: Practical allowlist CSP
This is what most teams end up shipping.
Example
Starting from the real header above, a practical Wistia-compatible version might look like this:
Content-Security-Policy:
default-src 'self' https://www.googletagmanager.com https://*.cookiebot.com https://*.google-analytics.com;
script-src 'self' 'nonce-YmNiMGE3NDgtYTc2Yi00MzAyLTg4NTYtZGQyOGU2ZTlkODBi' 'strict-dynamic'
https://www.googletagmanager.com
https://*.cookiebot.com
https://*.google-analytics.com
https://fast.wistia.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://fast.wistia.com
https://fast.wistia.net;
frame-src 'self'
https://consentcdn.cookiebot.com
https://fast.wistia.net;
media-src 'self'
https://fast.wistia.com
https://fast.wistia.net
https://embedwistia-a.akamaihd.net;
frame-ancestors 'none';
base-uri 'self';
form-action 'self';
object-src 'none';
Pros
- Fastest to implement. Usually you can get Wistia working in one pass.
- Easy to reason about operationally. If Wistia breaks, check blocked URI, add domain, move on.
- Works well with third-party-heavy sites. If your policy already includes GTM, analytics, cookie consent, chat widgets, and A/B testing tools, this fits the pattern.
Cons
- Host allowlists age badly. Third-party vendors change domains, add CDNs, or shift APIs.
- Easy to over-broaden. Teams often end up with
img-src https:andmedia-src https:just to stop the breakage. - Weakens the spirit of strict CSP. Especially if you start adding script hosts casually.
My opinion: this is acceptable if your site already has a “managed allowlist” CSP and you treat it as living infrastructure, not a one-time checkbox.
Option 2: Strict nonce-based CSP with targeted Wistia exceptions
If you already use nonces and strict-dynamic, I’d keep that model and resist turning script-src into a giant third-party whitelist.
The point of strict-dynamic is that nonce-trusted scripts can load their dependencies without relying on broad host allowlists in supporting browsers. That means you should prefer a trusted bootstrapping script you control, then add only the non-script directives Wistia truly needs.
Example
Content-Security-Policy:
default-src 'self';
script-src 'self' 'nonce-r4nd0m123' 'strict-dynamic';
style-src 'self' 'unsafe-inline';
img-src 'self' data: https:;
font-src 'self';
connect-src 'self' https://fast.wistia.com https://fast.wistia.net;
frame-src 'self' https://fast.wistia.net;
media-src 'self' https://fast.wistia.com https://fast.wistia.net https://embedwistia-a.akamaihd.net;
frame-ancestors 'none';
base-uri 'self';
form-action 'self';
object-src 'none';
Then load your embed bootstrap from a nonce-bearing script in your HTML:
<script nonce="{{ .CSPNonce }}">
window._wq = window._wq || [];
_wq.push({ id: "abc123xyz", options: { autoPlay: false } });
</script>
<script nonce="{{ .CSPNonce }}" src="/js/load-wistia.js"></script>
And in /js/load-wistia.js:
const s = document.createElement('script');
s.src = 'https://fast.wistia.com/player.js';
s.async = true;
document.head.appendChild(s);
Pros
- Cleaner security model. You trust your own nonce-bearing bootstrap, not every script from every allowed host.
- Better long-term discipline. Third-party scripts don’t automatically get a permanent seat in
script-src. - Fits modern CSP design better. Especially if you already avoid inline script except nonce-protected blocks.
Cons
- More setup complexity. You need nonce generation wired correctly into rendering.
- Browser behavior is more nuanced.
strict-dynamicsupport is not universal in every old environment. - Debugging can be trickier. A broken nonce pipeline causes weird failures fast.
My take: if you’ve already invested in strict CSP, don’t throw it away for one video provider. Keep script-src strict and add Wistia mainly in frame-src, connect-src, and media-src.
The iframe embed vs script embed question
This matters more than people think.
Iframe-style embeds
If you can use an iframe-only embed, CSP is usually easier:
frame-srcfor the player- maybe
img-srcandmedia-src - less JavaScript trust complexity on your page
Pros
- Cleaner isolation
- Lower risk to your page’s JS execution model
- Usually simpler CSP
Cons
- Less flexibility for custom player integrations
- May be harder to coordinate advanced events or styling
Script-driven embeds
These are common because they’re flexible and easy to drop into CMS content.
Pros
- Better customization
- Easier event hooks and player config
- More control over UX
Cons
- More CSP surface area
- More script trust decisions
- More likely to trigger “just add this domain too” policy creep
If your site is security-sensitive, I’d strongly prefer iframe-based embedding where product requirements allow it.
What I’d change in the real headertest-style policy
Using that real header as a starting point, I would not shove Wistia domains into default-src. That directive is already carrying too much weight.
Instead, I’d keep default-src 'self' as the long-term target and explicitly add Wistia only where needed:
script-src ... https://fast.wistia.com;
connect-src ... https://fast.wistia.com https://fast.wistia.net;
frame-src ... https://fast.wistia.net;
media-src 'self' https://fast.wistia.com https://fast.wistia.net https://embedwistia-a.akamaihd.net;
That’s better than relying on a broad default-src fallback. CSP gets safer when each resource type is declared intentionally.
Pros and cons side-by-side
Practical allowlist CSP
Pros
- Quick to ship
- Easy for mixed-skill teams
- Predictable for vendor-heavy stacks
Cons
- Tends to sprawl
- Easier to over-permit
- Harder to keep truly strict
Strict nonce-based CSP
Pros
- Stronger script execution control
- Better fit for modern hardened apps
- Limits third-party script trust
Cons
- More engineering effort
- Requires good nonce hygiene
- Can be painful in legacy templates or CMS platforms
My recommendation
If you run a content site, marketing site, or app with lots of third-party tags already in the page, use the practical allowlist CSP, but keep Wistia allowances narrow and resource-specific.
If you run a product app, admin dashboard, or anything handling sensitive workflows, use the strict nonce-based CSP and avoid broadening script-src more than necessary.
Either way:
- prefer explicit directives over
default-srcfallback - add
media-srcinstead of assumingdefault-srccoverage - test with real embeds, not just static markup
- monitor CSP violation reports before and after rollout
And one last opinionated point: if adding one video provider forces you to weaken half your policy, that’s not a Wistia problem alone. It usually means your CSP was already drifting toward “looks secure in a screenshot” territory.