Wistia Channels are easy to drop into a page. Getting them past a strict Content Security Policy is the part that usually wastes an afternoon.
If you embed a Wistia Channel and your CSP is even moderately locked down, you’ll usually hit one of these:
- the channel frame doesn’t render
- JavaScript inside the embed gets blocked
- thumbnails or poster images disappear
- analytics or websocket connections fail
- custom styling breaks
This guide is the practical version: what to allow, why, and copy-paste policies you can start with.
If you want to inspect your current CSP header quickly, I’d use HeaderTest. It’s handy for checking what your app is actually sending, not what you think it’s sending.
What Wistia Channels usually need
A Wistia Channel embed commonly pulls from Wistia-owned domains for:
- embed scripts
- iframe content
- images and thumbnails
- media delivery
- API or tracking requests
The exact domains can vary a bit over time, but in practice you’ll usually need to account for:
fast.wistia.comfast.wistia.netembedwistia-a.akamaihd.net
Depending on your setup, you may also need to permit generic HTTPS media or image sources if Wistia serves assets through CDN paths you don’t want to enumerate too tightly.
Minimum CSP directives to think about
For Wistia Channels, these are the directives that matter most:
script-srcframe-srcimg-srcmedia-srcconnect-srcstyle-src
If your policy uses default-src as a fallback, don’t assume that’s enough. Wistia embeds often need explicit per-directive allowances.
If you want a deeper refresher on directive behavior, csp-guide.com is a good reference.
Safe starting policy for Wistia Channels
This is the baseline I’d start with for a site embedding Wistia Channels.
Content-Security-Policy:
default-src 'self';
script-src 'self' https://fast.wistia.com;
frame-src 'self' https://fast.wistia.net;
img-src 'self' data: https://fast.wistia.com https://fast.wistia.net https://embedwistia-a.akamaihd.net;
media-src 'self' https://fast.wistia.com https://fast.wistia.net https://embedwistia-a.akamaihd.net;
connect-src 'self' https://fast.wistia.com https://fast.wistia.net;
style-src 'self' 'unsafe-inline';
object-src 'none';
base-uri 'self';
frame-ancestors 'self';
Why this works
script-srcallows Wistia’s embed JS.frame-srcallows the actual channel/player frame.img-srccovers thumbnails, poster frames, tracking pixels, and CDN-hosted images.media-srccovers video/audio delivery.connect-srccovers API/fetch/XHR requests.style-src 'unsafe-inline'is often needed if your app or embed flow relies on inline styles. If you can avoid it, do. But a lot of teams can’t without extra work.
Copy-paste examples by environment
1. HTML meta tag example
Use this only if you can’t control headers. Real CSP belongs in HTTP headers.
<meta
http-equiv="Content-Security-Policy"
content="
default-src 'self';
script-src 'self' https://fast.wistia.com;
frame-src 'self' https://fast.wistia.net;
img-src 'self' data: https://fast.wistia.com https://fast.wistia.net https://embedwistia-a.akamaihd.net;
media-src 'self' https://fast.wistia.com https://fast.wistia.net https://embedwistia-a.akamaihd.net;
connect-src 'self' https://fast.wistia.com https://fast.wistia.net;
style-src 'self' 'unsafe-inline';
object-src 'none';
base-uri 'self';
frame-ancestors 'self';
"
/>
2. Nginx
add_header Content-Security-Policy "default-src 'self'; script-src 'self' https://fast.wistia.com; frame-src 'self' https://fast.wistia.net; img-src 'self' data: https://fast.wistia.com https://fast.wistia.net https://embedwistia-a.akamaihd.net; media-src 'self' https://fast.wistia.com https://fast.wistia.net https://embedwistia-a.akamaihd.net; connect-src 'self' https://fast.wistia.com https://fast.wistia.net; style-src 'self' 'unsafe-inline'; object-src 'none'; base-uri 'self'; frame-ancestors 'self';" always;
3. Apache
Header always set Content-Security-Policy "default-src 'self'; script-src 'self' https://fast.wistia.com; frame-src 'self' https://fast.wistia.net; img-src 'self' data: https://fast.wistia.com https://fast.wistia.net https://embedwistia-a.akamaihd.net; media-src 'self' https://fast.wistia.com https://fast.wistia.net https://embedwistia-a.akamaihd.net; connect-src 'self' https://fast.wistia.com https://fast.wistia.net; style-src 'self' 'unsafe-inline'; object-src 'none'; base-uri 'self'; frame-ancestors 'self';"
4. Express / Node.js with Helmet
import helmet from "helmet";
import express from "express";
const app = express();
app.use(
helmet.contentSecurityPolicy({
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "https://fast.wistia.com"],
frameSrc: ["'self'", "https://fast.wistia.net"],
imgSrc: [
"'self'",
"data:",
"https://fast.wistia.com",
"https://fast.wistia.net",
"https://embedwistia-a.akamaihd.net",
],
mediaSrc: [
"'self'",
"https://fast.wistia.com",
"https://fast.wistia.net",
"https://embedwistia-a.akamaihd.net",
],
connectSrc: ["'self'", "https://fast.wistia.com", "https://fast.wistia.net"],
styleSrc: ["'self'", "'unsafe-inline'"],
objectSrc: ["'none'"],
baseUri: ["'self'"],
frameAncestors: ["'self'"],
},
})
);
5. Next.js custom header
// next.config.js
module.exports = {
async headers() {
return [
{
source: "/(.*)",
headers: [
{
key: "Content-Security-Policy",
value: [
"default-src 'self'",
"script-src 'self' https://fast.wistia.com",
"frame-src 'self' https://fast.wistia.net",
"img-src 'self' data: https://fast.wistia.com https://fast.wistia.net https://embedwistia-a.akamaihd.net",
"media-src 'self' https://fast.wistia.com https://fast.wistia.net https://embedwistia-a.akamaihd.net",
"connect-src 'self' https://fast.wistia.com https://fast.wistia.net",
"style-src 'self' 'unsafe-inline'",
"object-src 'none'",
"base-uri 'self'",
"frame-ancestors 'self'",
].join("; "),
},
],
},
];
},
};
If you already have a strict CSP
Most real apps already have a CSP and just need to extend it. Here’s a realistic example based on a production-style header, using the same structure as this real policy:
content-security-policy:
default-src 'self' https://www.googletagmanager.com https://*.cookiebot.com https://*.google-analytics.com;
script-src 'self' 'nonce-Yjc0N2FiMTMtYzYxNS00ZWI2LWIzZWYtOTQzMDFjMjMwZjUx' '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 support Wistia Channels, I’d extend it like this:
Content-Security-Policy:
default-src 'self' https://www.googletagmanager.com https://*.cookiebot.com https://*.google-analytics.com;
script-src 'self' 'nonce-Yjc0N2FiMTMtYzYxNS00ZWI2LWIzZWYtOTQzMDFjMjMwZjUx' '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: https://fast.wistia.com https://fast.wistia.net https://embedwistia-a.akamaihd.net;
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';
That’s usually enough without blowing the policy wide open.
Common CSP errors with Wistia Channels
Refused to load the frame
You’ll see something like:
Refused to frame 'https://fast.wistia.net/' because it violates the following Content Security Policy directive: "frame-src 'self'".
Fix:
frame-src 'self' https://fast.wistia.net;
Refused to load the script
Refused to load the script 'https://fast.wistia.com/assets/external/channel.js' because it violates the following Content Security Policy directive: "script-src 'self'".
Fix:
script-src 'self' https://fast.wistia.com;
Refused to load image or thumbnail
Refused to load the image 'https://embedwistia-a.akamaihd.net/...' because it violates the following Content Security Policy directive: "img-src 'self'".
Fix:
img-src 'self' data: https://fast.wistia.com https://fast.wistia.net https://embedwistia-a.akamaihd.net;
Video loads UI but media won’t play
That’s usually media-src.
Fix:
media-src 'self' https://fast.wistia.com https://fast.wistia.net https://embedwistia-a.akamaihd.net;
Good hardening choices
I’d keep these unless you have a specific reason not to:
object-src 'none';
base-uri 'self';
And if your pages should never be embedded by other sites:
frame-ancestors 'none';
If your own app embeds these pages in same-origin frames, use:
frame-ancestors 'self';
Report-Only first if you’re nervous
For production sites with a lot of moving parts, start with Content-Security-Policy-Report-Only before enforcing.
Content-Security-Policy-Report-Only:
default-src 'self';
script-src 'self' https://fast.wistia.com;
frame-src 'self' https://fast.wistia.net;
img-src 'self' data: https://fast.wistia.com https://fast.wistia.net https://embedwistia-a.akamaihd.net;
media-src 'self' https://fast.wistia.com https://fast.wistia.net https://embedwistia-a.akamaihd.net;
connect-src 'self' https://fast.wistia.com https://fast.wistia.net;
style-src 'self' 'unsafe-inline';
object-src 'none';
base-uri 'self';
frame-ancestors 'self';
That gives you violation data before users feel the pain.
My default recommendation
If you just want the short version, use this and adjust from there:
Content-Security-Policy:
default-src 'self';
script-src 'self' https://fast.wistia.com;
frame-src 'self' https://fast.wistia.net;
img-src 'self' data: https://fast.wistia.com https://fast.wistia.net https://embedwistia-a.akamaihd.net;
media-src 'self' https://fast.wistia.com https://fast.wistia.net https://embedwistia-a.akamaihd.net;
connect-src 'self' https://fast.wistia.com https://fast.wistia.net;
style-src 'self' 'unsafe-inline';
object-src 'none';
base-uri 'self';
frame-ancestors 'self';
That’s the version I’d try first for Wistia Channels on a normal app. Tight enough to be useful, not so tight that the embed breaks for dumb reasons.