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:

  1. Practical allowlist CSP
    Add the Wistia domains you need to script-src, frame-src, img-src, connect-src, and sometimes media-src.
  2. Strict nonce-based CSP with minimal host exceptions
    Keep a nonce-based script-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-dynamic
  • object-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.com
  • fast.wistia.net
  • embedwistia-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: and media-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-dynamic support 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-src for the player
  • maybe img-src and media-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-src fallback
  • add media-src instead of assuming default-src coverage
  • 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.