Instagram embeds are one of those things that look simple until CSP gets involved.
You paste the embed code, reload the page, and suddenly the post is blank, the console is yelling about blocked frames or scripts, and someone suggests adding https: to half your policy. That usually “works,” but it also wrecks the point of having CSP in the first place.
If you want Instagram embeds and a CSP that still means something, you need to decide which tradeoff you’re willing to accept.
The short version
For Instagram embeds, you’ll usually need to allow some combination of:
frame-srcfor Instagram’s embedded frame contentscript-srcfor Instagram’s embed script if you use their JavaScript-based embed flowimg-srcfor Instagram-hosted media and tracking pixelsconnect-srcin some setups, depending on what the embed script doesstyle-srconly if your own integration injects inline styles or third-party UI does something annoying
The exact hostnames can change over time, which is part of the problem. That means there are really a few strategies, each with different security and maintenance costs.
A good baseline: start strict, then open only what breaks
I like starting from a policy shaped roughly like this real-world example from headertest.com:
Content-Security-Policy:
default-src 'self' https://www.googletagmanager.com https://*.cookiebot.com https://*.google-analytics.com;
script-src 'self' 'nonce-OTYyMTQyOWEtZTc2Yi00ZDMyLWI4MWQtOTM1ZDBiNTU4YjZi' '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 sane starting point because it’s explicit. It doesn’t pretend all third-party content is harmless. To support Instagram, you extend the relevant directives instead of blowing a hole through default-src.
Option 1: Allow Instagram only in frame-src
This is the simplest option if your embed is just an iframe and you are not loading Instagram’s JavaScript.
Example:
Content-Security-Policy:
default-src 'self';
frame-src 'self' https://www.instagram.com;
img-src 'self' data: https:;
script-src 'self';
style-src 'self';
object-src 'none';
base-uri 'self';
form-action 'self';
Pros
- Smallest blast radius
- Easy to reason about
- Keeps
script-srctight - Best choice if your app renders a plain iframe and nothing else
Cons
- Often not enough for Instagram’s official embed flow
- May break if Instagram serves content from additional subdomains
- You still need to verify media and supporting requests in DevTools
When I’d use it
If I control the markup and can keep the embed as a plain iframe, this is my first attempt. If it works, great. If it doesn’t, I move to the next option instead of preemptively allowing a bunch of script origins.
Option 2: Allow Instagram embed script in script-src
A lot of teams use Instagram’s official embed markup plus their script. That means CSP has to allow that script source.
Typical shape:
Content-Security-Policy:
default-src 'self';
script-src 'self' https://www.instagram.com;
frame-src 'self' https://www.instagram.com;
img-src 'self' data: https:;
style-src 'self';
object-src 'none';
base-uri 'self';
form-action 'self';
If your page uses nonces, keep doing that. Don’t throw away a good script-src design just because a third-party embed showed up.
Example with a nonce-based setup:
Content-Security-Policy:
default-src 'self';
script-src 'self' 'nonce-r4nd0m' https://www.instagram.com;
frame-src 'self' https://www.instagram.com;
img-src 'self' data: https:;
style-src 'self';
object-src 'none';
base-uri 'self';
form-action 'self';
And your script tag:
<script async nonce="r4nd0m" src="https://www.instagram.com/embed.js"></script>
Pros
- Supports the official JavaScript-driven embed experience
- Still reasonably narrow
- Works well with nonce-based CSPs
Cons
- You’re trusting third-party JavaScript, not just framed content
- Script behavior may change without notice
- Some setups end up needing extra
img-srcorconnect-srcallowances
My take
This is where most production sites land. It’s acceptable, but I treat third-party script allowances as a real security decision, not a checkbox.
Option 3: Allow broader Instagram-related hosts
Sometimes the embed script or rendered content pulls from more than www.instagram.com. Teams then start adding subdomains or related CDN hosts.
That can look like this:
Content-Security-Policy:
default-src 'self';
script-src 'self' https://www.instagram.com https://*.instagram.com;
frame-src 'self' https://www.instagram.com https://*.instagram.com;
img-src 'self' data: https: https://*.cdninstagram.com https://*.instagram.com;
connect-src 'self' https://www.instagram.com https://*.instagram.com;
style-src 'self';
object-src 'none';
base-uri 'self';
form-action 'self';
Pros
- Fewer random production breakages
- More resilient to vendor-side hostname changes
- Easier for teams that don’t want to babysit CSP reports every week
Cons
- Noticeably weaker than single-origin allowlists
- Wildcards are easy to overuse
- You may end up permitting way more than the embed actually needs
When it makes sense
If you’ve verified that Instagram is legitimately using multiple subdomains in your environment, this is practical. I’d still keep it scoped to the directives that need it. Don’t copy the wildcard into default-src just because it’s convenient.
Option 4: The lazy fix — broad https: allowances
You’ll see policies like this in the wild:
Content-Security-Policy:
default-src 'self';
script-src 'self' https:;
frame-src 'self' https:;
img-src 'self' data: https:;
style-src 'self' 'unsafe-inline';
Yes, the embed will probably work.
Pros
- Fast
- Low maintenance
- Console errors go away quickly
Cons
- Barely a meaningful CSP anymore
- Any HTTPS third-party script or frame is now fair game
- Makes incident response and policy review much harder
My opinion
I hate this pattern. It turns CSP into theater. If your only goal is “stop browser warnings,” fine, but call it what it is: a compatibility policy, not a security policy.
Option 5: Isolate embeds on a dedicated page or subdomain
If Instagram embeds are business-critical and you don’t want to pollute your main app policy, isolate them.
For example:
www.example.comkeeps a strict CSPembeds.example.comgets a more permissive CSP for social widgets
Main app:
Content-Security-Policy:
default-src 'self';
script-src 'self' 'nonce-r4nd0m' 'strict-dynamic';
style-src 'self';
img-src 'self' data:;
connect-src 'self' https://api.example.com;
frame-ancestors 'none';
object-src 'none';
base-uri 'self';
Embed page:
Content-Security-Policy:
default-src 'self';
script-src 'self' https://www.instagram.com;
frame-src 'self' https://www.instagram.com;
img-src 'self' data: https: https://*.instagram.com https://*.cdninstagram.com;
connect-src 'self' https://www.instagram.com https://*.instagram.com;
style-src 'self' 'unsafe-inline';
object-src 'none';
base-uri 'self';
Pros
- Strongest separation of risk
- Keeps your main application clean
- Easier to audit and explain
Cons
- More infrastructure and deployment complexity
- More moving parts for cookies, routing, and styling
- Can be overkill for small sites
When I’d choose it
For larger apps, multi-team environments, or anything handling sensitive user workflows, this is often the best architecture. Third-party widgets are messy. Giving them their own sandboxed home is a sane move.
Directives that usually matter most
If you need a refresher on directive behavior, https://csp-guide.com is useful, and the canonical reference is the MDN CSP documentation.
A few practical notes:
frame-src
If the embed renders inside an iframe, this is usually the first thing that breaks.
frame-src 'self' https://www.instagram.com;
script-src
Needed if you load Instagram’s embed JavaScript.
script-src 'self' 'nonce-r4nd0m' https://www.instagram.com;
If you already use 'strict-dynamic', be careful. A nonce-bearing bootstrap script can change how host allowlists are interpreted in modern browsers. Don’t cargo-cult that combination without understanding it.
img-src
This one gets overlooked. Instagram embeds often need remote images.
A lot of production policies already do this, like the headertest.com example:
img-src 'self' data: https:;
That’s convenient, but broad. If you want tighter control, prefer explicit hosts once you know them.
connect-src
Sometimes required for background requests, telemetry, or API fetches made by the embed script.
connect-src 'self' https://www.instagram.com https://*.instagram.com;
A practical rollout pattern
This is the least painful way I’ve found to ship third-party embeds under CSP:
- Start with your current strict policy.
- Add only
frame-srcfor Instagram. - Test.
- If using
embed.js, addscript-srcfor the exact host. - Watch DevTools and CSP reports for blocked
img-srcandconnect-src. - Add the narrowest host allowances that fix real breakage.
- Re-test in multiple browsers.
If you support legacy browsers, expect weirdness. CSP behavior is much better than it used to be, but third-party embed code still finds creative ways to be annoying.
Recommended policy for most developer teams
If you’re using Instagram’s official embed script and want a balanced policy, I’d start here:
Content-Security-Policy:
default-src 'self';
script-src 'self' 'nonce-r4nd0m' https://www.instagram.com;
frame-src 'self' https://www.instagram.com;
img-src 'self' data: https:;
connect-src 'self' https://www.instagram.com;
style-src 'self';
object-src 'none';
base-uri 'self';
form-action 'self';
frame-ancestors 'none';
Then tighten img-src and connect-src later if your reporting data shows the exact hosts in use.
That’s the real comparison here:
- Tight single-origin policy: best security, more maintenance
- Broader wildcard policy: easier operations, weaker trust boundary
- Dedicated embed origin: best long-term design, more complexity
https:everywhere: fast, but mostly security cosplay
If I had to pick one for a serious production app, I’d choose either the narrow allowlist or a dedicated embed origin. Instagram embeds are not special enough to justify throwing away a good CSP.