Facebook video embeds are one of those things that look trivial until CSP starts blocking them.
You paste Facebook’s embed code, reload, and suddenly your console is full of Refused to frame or Refused to load the script errors. The fix is usually small, but the exact directives matter. If you loosen the wrong thing, you end up with a policy that “works” and quietly stops protecting anything useful.
Here’s the practical reference I wish more teams had handy.
The short version
For a basic Facebook video embed, you’ll usually need to allow:
frame-srcfor Facebook’s embed/player originsscript-srcif you use Facebook’s SDK-based embedconnect-srcin some SDK casesimg-srcif Facebook assets are fetched- possibly
style-srcif your page or widget injects inline styles
If you’re embedding with a plain <iframe>, start with frame-src. That’s the cleanest option.
Option 1: Facebook video embed via iframe
This is the easiest setup to secure.
Typical embed code looks like this:
<iframe
src="https://www.facebook.com/plugins/video.php?href=https%3A%2F%2Fwww.facebook.com%2Ffacebookapp%2Fvideos%2F10153231379946729%2F&show_text=0&width=560"
width="560"
height="315"
style="border:none;overflow:hidden"
scrolling="no"
frameborder="0"
allowfullscreen="true"
allow="autoplay; clipboard-write; encrypted-media; picture-in-picture; web-share">
</iframe>
For this style of embed, a minimal CSP often looks like:
Content-Security-Policy:
default-src 'self';
frame-src 'self' https://www.facebook.com;
img-src 'self' data: https:;
style-src 'self' 'unsafe-inline';
object-src 'none';
base-uri 'self';
Why this works
frame-src https://www.facebook.comallows the iframe itselfimg-src https:covers remote images if your page loads previews or related assetsstyle-src 'unsafe-inline'is only there because your iframe example uses an inlinestyle=""attribute
If you remove the inline style attribute from the iframe, you can tighten that up:
<iframe
class="fb-video-frame"
src="https://www.facebook.com/plugins/video.php?href=https%3A%2F%2Fwww.facebook.com%2Ffacebookapp%2Fvideos%2F10153231379946729%2F&show_text=0&width=560"
width="560"
height="315"
scrolling="no"
frameborder="0"
allowfullscreen="true"
allow="autoplay; clipboard-write; encrypted-media; picture-in-picture; web-share">
</iframe>
.fb-video-frame {
border: none;
overflow: hidden;
}
Then your CSP can become:
Content-Security-Policy:
default-src 'self';
frame-src 'self' https://www.facebook.com;
img-src 'self' data: https:;
style-src 'self';
object-src 'none';
base-uri 'self';
That’s better. I always prefer removing inline style allowances when I can.
Option 2: Facebook embed using the SDK script
Some teams use Facebook’s JavaScript SDK instead of a raw iframe.
Example markup:
<div id="fb-root"></div>
<script async defer crossorigin="anonymous"
src="https://connect.facebook.net/en_US/sdk.js#xfbml=1&version=v19.0">
</script>
<div class="fb-video"
data-href="https://www.facebook.com/facebookapp/videos/10153231379946729/"
data-width="560"
data-allowfullscreen="true">
</div>
For this, you need more than frame-src.
Content-Security-Policy:
default-src 'self';
script-src 'self' https://connect.facebook.net;
frame-src 'self' https://www.facebook.com;
connect-src 'self' https://connect.facebook.net https://www.facebook.com;
img-src 'self' data: https:;
style-src 'self';
object-src 'none';
base-uri 'self';
This is the version people usually miss: they allow the frame, but forget the SDK script is loaded from connect.facebook.net.
Copy-paste policies
Minimal CSP for iframe-only Facebook video embeds
Use this if the page only needs the iframe embed and no Facebook SDK.
Content-Security-Policy:
default-src 'self';
frame-src 'self' https://www.facebook.com;
object-src 'none';
base-uri 'self';
That’s the smallest starting point. If your page also loads external images, fonts, or styles, add those separately.
Safer practical CSP for a normal page with iframe embeds
Content-Security-Policy:
default-src 'self';
script-src 'self';
style-src 'self';
img-src 'self' data: https:;
font-src 'self';
connect-src 'self';
frame-src 'self' https://www.facebook.com;
object-src 'none';
base-uri 'self';
form-action 'self';
frame-ancestors 'none';
I like this shape because it stays explicit. default-src is not a substitute for setting the directives you actually care about.
CSP for Facebook SDK + video embeds
Content-Security-Policy:
default-src 'self';
script-src 'self' https://connect.facebook.net;
style-src 'self';
img-src 'self' data: https:;
connect-src 'self' https://connect.facebook.net https://www.facebook.com;
frame-src 'self' https://www.facebook.com;
font-src 'self';
object-src 'none';
base-uri 'self';
form-action 'self';
frame-ancestors 'none';
Nonce-based CSP with your own scripts
If your app already uses nonces, keep doing that. Don’t fall back to 'unsafe-inline' for convenience.
Example HTML:
<script nonce="{{ .CSPNonce }}">
window.videoEmbedEnabled = true;
</script>
<script nonce="{{ .CSPNonce }}" async defer crossorigin="anonymous"
src="https://connect.facebook.net/en_US/sdk.js#xfbml=1&version=v19.0">
</script>
Matching CSP:
Content-Security-Policy:
default-src 'self';
script-src 'self' 'nonce-{{RANDOM_NONCE}}' https://connect.facebook.net;
style-src 'self';
img-src 'self' data: https:;
connect-src 'self' https://connect.facebook.net https://www.facebook.com;
frame-src 'self' https://www.facebook.com;
object-src 'none';
base-uri 'self';
If you use nonce-based CSP heavily, read up on directive behavior at csp-guide.com. The difference between “works in dev” and “holds up in production” usually comes down to understanding fallback behavior and script trust propagation.
Real-world example: adding Facebook to an existing CSP
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-ZGU2Y2YyOTgtMWVmYi00NTFjLWIzMDgtMDQ3M2EzNWNkN2Qx' '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://collect.tallytics.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'
If you wanted to support a Facebook video iframe on a site with that policy, the smallest likely change is:
content-security-policy: default-src 'self' https://www.googletagmanager.com https://*.cookiebot.com https://*.google-analytics.com; script-src 'self' 'nonce-ZGU2Y2YyOTgtMWVmYi00NTFjLWIzMDgtMDQ3M2EzNWNkN2Qx' '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://collect.tallytics.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://www.facebook.com; frame-ancestors 'none'; base-uri 'self'; form-action 'self'; object-src 'none'
If you also load the Facebook SDK script, then add https://connect.facebook.net to script-src, and probably connect-src too.
That’s the pattern I use in production: make the smallest possible change, verify exactly what was blocked, then expand only if the browser proves you need it.
Common CSP errors and fixes
Refused to frame 'https://www.facebook.com/' because it violates frame-src
You need:
frame-src https://www.facebook.com
If you only set default-src, browsers may still block the frame depending on your policy shape and fallback behavior.
Refused to load the script 'https://connect.facebook.net/...'
You need:
script-src https://connect.facebook.net
Or add it to your existing script-src list.
Refused to connect to https://www.facebook.com/...
Usually SDK-related. Add:
connect-src https://www.facebook.com https://connect.facebook.net
Inline style blocked on the iframe
If your embed markup includes:
style="border:none;overflow:hidden"
Then either:
- allow inline styles:
style-src 'self' 'unsafe-inline'
or, better:
- move styles into your stylesheet and keep:
style-src 'self'
I’d pick option 2 every time unless you’re stuck inside a CMS that insists on inline junk.
A few gotchas
frame-src vs child-src
Use frame-src. Older docs and examples sometimes mention child-src, but for iframe control, frame-src is the one you want in modern policies.
Don’t whitelist all of Facebook unless you have to
This is sloppy:
frame-src https://*.facebook.com
Maybe you need it. Usually you don’t. Start with:
frame-src https://www.facebook.com
Same for scripts. Don’t spray wildcards around because one blog post said so in 2019.
X-Frame-Options does not control what your page can embed
I still see this confusion. X-Frame-Options controls whether your page can be framed by someone else. It does not allow Facebook embeds. That’s frame-src.
Recommended starting point
If I were adding Facebook video embeds to a reasonably locked-down site today, I’d start here for iframe-only embeds:
Content-Security-Policy:
default-src 'self';
script-src 'self';
style-src 'self';
img-src 'self' data: https:;
font-src 'self';
connect-src 'self';
frame-src 'self' https://www.facebook.com;
object-src 'none';
base-uri 'self';
form-action 'self';
frame-ancestors 'none';
And for SDK-based embeds:
Content-Security-Policy:
default-src 'self';
script-src 'self' https://connect.facebook.net;
style-src 'self';
img-src 'self' data: https:;
font-src 'self';
connect-src 'self' https://connect.facebook.net https://www.facebook.com;
frame-src 'self' https://www.facebook.com;
object-src 'none';
base-uri 'self';
form-action 'self';
frame-ancestors 'none';
That gets you a working baseline without turning CSP into decorative security.