Bandcamp embeds look simple right up until your CSP starts blocking them.
You paste the iframe, refresh, and get a blank box or a console full of violations. I’ve seen this pattern a lot: teams lock down CSP correctly, then third-party embeds get boladted on later and nobody updates the policy with any care. The result is usually one of two bad outcomes:
- the embed breaks, or
- somebody “fixes” it by allowing way too much.
Bandcamp is pretty tame compared to ad tech or social widgets, but there are still a few easy mistakes that keep showing up. Here’s what tends to go wrong, and how I’d fix it without turning your CSP into allow basically everything.
The basic Bandcamp embed
A typical Bandcamp embed looks like this:
<iframe
style="border: 0; width: 350px; height: 470px;"
src="https://bandcamp.com/EmbeddedPlayer/album=1234567890/size=large/bgcol=ffffff/linkcol=0687f5/tracklist=false/artwork=small/transparent=true/"
seamless>
<a href="https://artistname.bandcamp.com/album/example-album">Example Album by Artist Name</a>
</iframe>
At minimum, your CSP needs to allow Bandcamp as a framed source. That usually means frame-src.
A reasonable starting point:
Content-Security-Policy:
default-src 'self';
frame-src 'self' https://bandcamp.com;
img-src 'self' data: https:;
style-src 'self' 'unsafe-inline';
script-src 'self';
object-src 'none';
base-uri 'self';
frame-ancestors 'none';
That said, “reasonable starting point” is not the same thing as “done”.
Mistake #1: Forgetting frame-src
This is the classic one.
You have a strict CSP like this:
Content-Security-Policy: default-src 'self'; object-src 'none'; base-uri 'self';
Then you add a Bandcamp iframe and wonder why it won’t render.
If frame-src isn’t set, the browser falls back to default-src. Since default-src 'self' doesn’t include Bandcamp, the iframe gets blocked.
Fix
Explicitly allow Bandcamp in frame-src:
Content-Security-Policy:
default-src 'self';
frame-src 'self' https://bandcamp.com;
object-src 'none';
base-uri 'self';
If you want a deeper refresher on CSP fallback behavior, csp-guide.com is a good reference.
Mistake #2: Using child-src and assuming that’s enough
Some codebases still use child-src because it used to control nested browsing contexts and workers. Modern CSP is clearer if you use the more specific directives.
If you only have:
Content-Security-Policy:
default-src 'self';
child-src https://bandcamp.com;
you may get inconsistent behavior depending on browser support and how the rest of your policy is written.
Fix
Use frame-src for Bandcamp embeds.
Content-Security-Policy:
default-src 'self';
frame-src https://bandcamp.com;
You can keep child-src if you have legacy reasons, but I wouldn’t rely on it as the main control for embeds anymore.
Mistake #3: Allowing *.bandcamp.com when you only need bandcamp.com
This one isn’t catastrophic, but it’s lazy. I get why people do it: wildcard subdomains feel safer when you’re not sure what the provider uses. But wildcards expand your trust boundary, and you should do that only when you actually need it.
Most standard Bandcamp embeds load from https://bandcamp.com/EmbeddedPlayer/..., not from random subdomains.
Fix
Start narrow:
frame-src 'self' https://bandcamp.com;
Only widen it if you confirm Bandcamp is actually loading framed content from somewhere else.
Same rule I use everywhere: don’t cargo-cult wildcards into CSP.
Mistake #4: Adding Bandcamp to script-src
I see this a lot when someone is debugging fast and not reading the violation closely.
The embed is an iframe. It is not a script tag you host directly on your page. If the browser says the frame was blocked, changing script-src won’t help.
Bad fix:
script-src 'self' https://bandcamp.com;
That just expands script trust for no benefit.
Fix
Put the allowance in the right directive:
frame-src 'self' https://bandcamp.com;
If you’re not sure which directive is actually causing the problem, test the live headers with a tool like HeaderTest. I like checking the effective policy there before I start changing directives blindly.
For example, this real CSP from HeaderTest itself is locked down in the way I’d expect from a production app:
content-security-policy: default-src 'self' https://www.googletagmanager.com https://*.cookiebot.com https://*.google-analytics.com; script-src 'self' 'nonce-YjBmNmQ1YmQtMWI5Mi00ZWJiLWI1MjMtYWJiNWU2ZWVkNmYx' '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'
Notice how frame-src is handled separately. That’s the mindset you want.
Mistake #5: Blocking Bandcamp artwork with an overly strict img-src
Sometimes the iframe itself loads fine, but album art or other media inside the player appears broken. Whether your page-level CSP affects those assets depends on how the content is loaded, but I’ve seen developers misdiagnose image issues and “fix” them in the wrong place.
If your own page also displays Bandcamp-related images outside the iframe, this policy will block them:
img-src 'self';
Fix
If you render Bandcamp images directly on your own page, allow the actual image origins you use. If you don’t know all of them yet, a pragmatic temporary step is:
img-src 'self' data: https:;
That’s broader than ideal, but still common in production. The HeaderTest policy above uses exactly that pattern.
If you can be more specific, be more specific.
Mistake #6: Forgetting frame-ancestors and thinking it controls outgoing embeds
This misunderstanding causes a lot of wasted time.
frame-ancestors controls who can embed your page. It does not control which third-party frames you embed.
So this does nothing for Bandcamp playback inside your page:
frame-ancestors https://bandcamp.com;
Fix
Use the right mental model:
frame-src: what your page may load in iframesframe-ancestors: who may embed your page
For a site that shouldn’t be framed by others, I usually like:
frame-ancestors 'none';
That’s also what the HeaderTest policy uses.
Mistake #7: Keeping a strict CSP but leaving a wide-open iframe sandbox
CSP is only part of the embed story. If you’re embedding third-party content, you should think about iframe sandboxing too.
A lot of Bandcamp snippets don’t include a sandbox attribute at all. That means the iframe gets the default browser behavior for a normal cross-origin frame.
Fix
If your Bandcamp embed still works with a sandbox, use one. Start minimal and loosen only if needed:
<iframe
src="https://bandcamp.com/EmbeddedPlayer/album=1234567890/size=large/bgcol=ffffff/linkcol=0687f5/tracklist=false/artwork=small/transparent=true/"
sandbox="allow-scripts allow-same-origin allow-popups"
loading="lazy"
referrerpolicy="strict-origin-when-cross-origin"
style="border:0; width:350px; height:470px;">
</iframe>
A few notes:
allow-scriptsis often required for rich embeds.allow-same-originweakens sandbox isolation, so don’t add it automatically without testing. Some embeds need it, some don’t.allow-popupsmay be necessary if users can click through to Bandcamp.
I’d test with fewer permissions first:
sandbox="allow-scripts allow-popups"
Then add allow-same-origin only if the player breaks.
Mistake #8: Shipping changes without Report-Only first
If your site already has a production CSP, changing it directly can break legitimate flows you forgot about. This gets worse when marketing pages, blog templates, and custom CMS blocks all behave slightly differently.
Fix
Try Bandcamp changes in Report-Only before enforcing them:
Content-Security-Policy-Report-Only:
default-src 'self';
frame-src 'self' https://bandcamp.com;
object-src 'none';
base-uri 'self';
report-to default-endpoint;
Then watch reports and browser console output. Once you know the embed works, move it into the enforced policy.
Mistake #9: Copy-pasting a giant CSP exception block from another provider
I’ve seen people paste in allowances for script-src, connect-src, media-src, font-src, and style-src because “that’s what we did for YouTube.”
Bandcamp is not YouTube. Don’t import somebody else’s threat model and call it done.
Fix
Add only what Bandcamp actually needs for your use case. For a plain iframe embed, that may be as little as:
Content-Security-Policy:
default-src 'self';
frame-src 'self' https://bandcamp.com;
img-src 'self' data: https:;
style-src 'self' 'unsafe-inline';
object-src 'none';
base-uri 'self';
frame-ancestors 'none';
Then tighten from there based on what your page itself requires.
A practical policy example
If I were adding a Bandcamp embed to a fairly normal content site, I’d start here:
Content-Security-Policy:
default-src 'self';
frame-src 'self' https://bandcamp.com;
img-src 'self' data: https:;
style-src 'self' 'unsafe-inline';
script-src 'self';
object-src 'none';
base-uri 'self';
form-action 'self';
frame-ancestors 'none';
And the embed:
<iframe
src="https://bandcamp.com/EmbeddedPlayer/album=1234567890/size=large/bgcol=ffffff/linkcol=0687f5/tracklist=false/artwork=small/transparent=true/"
title="Bandcamp album player"
loading="lazy"
sandbox="allow-scripts allow-popups"
referrerpolicy="strict-origin-when-cross-origin"
style="border:0; width:350px; height:470px;">
</iframe>
Then I’d test:
- Does the frame load?
- Does playback work?
- Do click-through links open correctly?
- Does sandboxing break anything?
- Are there console violations for
frame-src,img-src, or something else?
That last point matters. Read the violation. Don’t guess. CSP debugging gets ugly when people start changing five directives at once and then forget which one actually fixed it.
Bandcamp embeds usually don’t need a huge policy exception. Most breakage comes from putting the allowance in the wrong directive, overusing wildcards, or panicking and broadening the policy too far. Keep it scoped, test with real browser errors, and treat third-party embeds like the trust boundary they are.