Mux is easy to drop into a page. Getting the CSP right is the part that usually wastes time.
The main problem: “Mux embed” can mean a few different things:
- a plain
<iframe> - a custom player using Mux-hosted video
- a player library that pulls thumbnails, HLS manifests, segments, captions, and telemetry
- a setup mixed into an existing CSP that already has analytics, consent tools, and nonce-based scripts
I’ve had to debug all of these, and the failure mode is always the same: the video UI renders, then playback, poster images, subtitles, or analytics quietly break because one directive is missing.
Here’s the practical reference.
The shortest answer
If you’re embedding Mux with an iframe, you usually need frame-src.
If you’re playing Mux video directly in a player, you usually need:
media-srcimg-srcconnect-src
If you load a Mux player script from a CDN, you also need script-src and possibly style-src.
Official docs:
- https://www.mux.com/docs
- https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy
For deeper CSP directive explanations, https://csp-guide.com is solid.
Start from your current CSP, not from scratch
A real-world header often already looks like this:
Content-Security-Policy: default-src 'self' https://www.googletagmanager.com https://*.cookiebot.com https://*.google-analytics.com; script-src 'self' 'nonce-NzUwMzYxYTgtNDdlNy00Y2FmLWI1N2YtZDY0ODhkY2VkYjg2' '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 good baseline because it shows how most production CSPs evolve: analytics, consent, forms, websocket endpoints, and a bunch of tightly scoped directives.
You do not want to replace a policy like this with some giant permissive default-src https: 'unsafe-inline' 'unsafe-eval'. Just add the Mux pieces you actually need.
Option 1: CSP for a Mux iframe embed
If your page embeds Mux in an iframe, add the Mux iframe origin to frame-src.
Example:
<iframe
src="https://player.mux.com/VIDEO_ID"
allow="accelerometer; gyroscope; autoplay; encrypted-media; picture-in-picture"
allowfullscreen
></iframe>
CSP:
Content-Security-Policy:
default-src 'self';
frame-src 'self' https://player.mux.com;
object-src 'none';
base-uri 'self';
frame-ancestors 'none';
If you’re merging that into an existing policy, it looks more like this:
Content-Security-Policy: default-src 'self' https://www.googletagmanager.com https://*.cookiebot.com https://*.google-analytics.com; script-src 'self' 'nonce-abc123' '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 https://player.mux.com; frame-ancestors 'none'; base-uri 'self'; form-action 'self'; object-src 'none'
That’s the minimum for the iframe itself.
If the iframe is fully isolated and Mux handles playback inside it, your parent page usually does not need media-src for Mux. The iframe’s own document enforces its own CSP, not yours.
Option 2: CSP for direct Mux playback in your own player
This is where people miss directives.
A player using Mux-hosted media commonly needs:
img-srcfor poster images and thumbnailsmedia-srcfor media filesconnect-srcfor manifests, segment fetches, text tracks, and telemetry APIs
A practical starting point:
Content-Security-Policy:
default-src 'self';
img-src 'self' data: https://image.mux.com;
media-src https://stream.mux.com;
connect-src 'self' https://stream.mux.com https://image.mux.com;
object-src 'none';
base-uri 'self';
frame-ancestors 'none';
Example HTML:
<video
controls
poster="https://image.mux.com/VIDEO_ID/thumbnail.webp?time=0"
preload="metadata"
>
<source
src="https://stream.mux.com/VIDEO_ID.m3u8"
type="application/x-mpegURL"
/>
</video>
A stricter merged version based on the sample production header:
Content-Security-Policy: default-src 'self' https://www.googletagmanager.com https://*.cookiebot.com https://*.google-analytics.com; script-src 'self' 'nonce-NzUwMzYxYTgtNDdlNy00Y2FmLWI1N2YtZDY0ODhkY2VkYjg2' '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: https://image.mux.com; 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://stream.mux.com https://image.mux.com; media-src https://stream.mux.com; frame-src 'self' https://consentcdn.cookiebot.com; frame-ancestors 'none'; base-uri 'self'; form-action 'self'; object-src 'none'
Why both media-src and connect-src? Because modern players often fetch HLS manifests and segments via APIs or XHR/fetch paths that hit connect-src, while actual media loading can still be checked against media-src. Browser behavior around streaming can be annoyingly implementation-specific. I usually allow both for the stream host and move on with my life.
Option 3: CSP for mux-player
If you use the web component:
<script
type="module"
src="https://unpkg.com/@mux/mux-player"
></script>
<mux-player
playback-id="VIDEO_ID"
metadata-video-title="Demo"
></mux-player>
You need to account for the script origin too.
Example CSP:
Content-Security-Policy:
default-src 'self';
script-src 'self' https://unpkg.com;
img-src 'self' data: https://image.mux.com;
media-src https://stream.mux.com;
connect-src 'self' https://stream.mux.com https://image.mux.com;
style-src 'self' 'unsafe-inline';
object-src 'none';
base-uri 'self';
frame-ancestors 'none';
If your site already uses nonces and 'strict-dynamic', don’t casually bolt on random host allowlists unless you understand the interaction. With nonce-based CSP, trusted scripts can load further scripts dynamically. That can be great, but it also means debugging gets less obvious.
If you can self-host the player bundle, I generally prefer it. Fewer third-party script origins means a cleaner policy.
Copy-paste examples by use case
1. Mux iframe only
Content-Security-Policy: default-src 'self'; frame-src 'self' https://player.mux.com; object-src 'none'; base-uri 'self'; frame-ancestors 'none'
2. Native video tag with Mux HLS + poster
Content-Security-Policy: default-src 'self'; img-src 'self' data: https://image.mux.com; media-src https://stream.mux.com; connect-src 'self' https://stream.mux.com https://image.mux.com; object-src 'none'; base-uri 'self'; frame-ancestors 'none'
3. Existing production CSP with Mux added
Content-Security-Policy: default-src 'self' https://www.googletagmanager.com https://*.cookiebot.com https://*.google-analytics.com; script-src 'self' 'nonce-abc123' '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: https://image.mux.com; 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://stream.mux.com https://image.mux.com; media-src https://stream.mux.com; frame-src 'self' https://consentcdn.cookiebot.com https://player.mux.com; frame-ancestors 'none'; base-uri 'self'; form-action 'self'; object-src 'none'
Common mistakes
Forgetting frame-src
If the iframe is blocked, the browser console will usually tell you immediately. Easy one.
Allowing only media-src
I see this a lot with HLS playback. The video doesn’t fully start because the player fetches manifests or related assets under connect-src.
Using img-src https:
This works, but it’s broad. If you know you only need Mux thumbnails, use:
img-src 'self' data: https://image.mux.com;
That’s cleaner than letting every HTTPS image load forever.
Assuming default-src covers everything
It doesn’t, at least not in the way people expect once explicit directives exist. If you define connect-src, then default-src no longer governs connections. Same for img-src, media-src, and frame-src.
Breaking your nonce setup
If your app already has nonce-based inline scripts with 'strict-dynamic', don’t start tossing 'unsafe-inline' into script-src just to make a third-party embed work. Fix the actual source list or integration pattern.
Debugging checklist
When a Mux embed fails under CSP, I check these in order:
-
Iframe blocked?
Addhttps://player.mux.comtoframe-src. -
Poster missing?
Addhttps://image.mux.comtoimg-src. -
Playback fails or stalls?
Addhttps://stream.mux.comto bothmedia-srcandconnect-src. -
Custom player script blocked?
Add its CDN toscript-src, or self-host it. -
Still broken?
Use a temporaryContent-Security-Policy-Report-Onlyheader and inspect violations.
Example report-only header:
Content-Security-Policy-Report-Only: default-src 'self'; img-src 'self' data: https://image.mux.com; media-src https://stream.mux.com; connect-src 'self' https://stream.mux.com https://image.mux.com; frame-src 'self' https://player.mux.com
That lets you see what the browser would block without actually breaking production traffic.
My default recommendation
For most teams, I’d start with this and tighten later if needed:
Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https://image.mux.com; media-src https://stream.mux.com; connect-src 'self' https://stream.mux.com https://image.mux.com; frame-src 'self' https://player.mux.com; object-src 'none'; base-uri 'self'; frame-ancestors 'none'
Then remove what you don’t use:
- no iframe? drop
https://player.mux.com - no poster images? drop
https://image.mux.comfromimg-src - self-hosted player assets? keep
script-srctighter
That’s the whole game with CSP: be explicit, test the real embed path, and resist the urge to “fix” things with wildcards.