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-src
  • img-src
  • connect-src

If you load a Mux player script from a CDN, you also need script-src and possibly style-src.

Official docs:

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-src for poster images and thumbnails
  • media-src for media files
  • connect-src for 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:

  1. Iframe blocked?
    Add https://player.mux.com to frame-src.

  2. Poster missing?
    Add https://image.mux.com to img-src.

  3. Playback fails or stalls?
    Add https://stream.mux.com to both media-src and connect-src.

  4. Custom player script blocked?
    Add its CDN to script-src, or self-host it.

  5. Still broken?
    Use a temporary Content-Security-Policy-Report-Only header 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.com from img-src
  • self-hosted player assets? keep script-src tighter

That’s the whole game with CSP: be explicit, test the real embed path, and resist the urge to “fix” things with wildcards.