CSP for Eventbrite Embeds: Common Mistakes and Fixes

Table of Contents

Embedding Eventbrite looks simple right up until your CSP blocks it in three different ways and the browser gives you one vague console error.

I’ve seen this pattern a lot: a team has a decent CSP already, they drop in an Eventbrite widget or checkout embed, and suddenly registrations stop rendering. Then someone “fixes” it by adding https: everywhere or turning on 'unsafe-inline' for scripts. That gets the feature live, but it also guts the policy.

Here’s where people usually get CSP wrong with Eventbrite embeds, and how to fix it without setting your policy on fire.

The baseline problem

A typical production CSP often looks something like this real 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-YjllN2FlODUtMWExNC00YmNhLWI4MDAtODQyZTRhN2RlMzZk' '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 policy is already fairly locked down. It allows analytics, consent tooling, and same-origin resources. It does not allow Eventbrite scripts, frames, form posts, or API calls. So the embed fails, exactly as it should.

Mistake #1: Only adding Eventbrite to default-src

This is probably the most common misunderstanding.

People add something like this:

default-src 'self' https://www.eventbrite.com;

…and expect the embed to work.

It won’t, because modern CSP is directive-specific. If script-src, frame-src, or connect-src are present, the browser uses those instead of default-src for those resource types.

Fix

Add Eventbrite only to the directives it actually needs.

A common starting point looks like this:

Content-Security-Policy:
  default-src 'self';
  script-src 'self' 'nonce-{RANDOM}' 'strict-dynamic' https://www.eventbrite.com;
  frame-src 'self' https://www.eventbrite.com;
  connect-src 'self' https://www.eventbrite.com;
  img-src 'self' data: https:;
  style-src 'self' 'unsafe-inline';
  font-src 'self';
  base-uri 'self';
  form-action 'self' https://www.eventbrite.com;
  object-src 'none';
  frame-ancestors 'none';

That’s still probably incomplete for your exact embed flow, but it fixes the “why didn’t default-src work?” problem.

If you want ready-to-use patterns for comparison, https://csp-examples.com is useful.

Mistake #2: Forgetting frame-src

A lot of Eventbrite embeds render inside an iframe or open a checkout flow that depends on framed content. If frame-src doesn’t explicitly allow Eventbrite, the browser blocks it.

Typical console error:

Refused to frame 'https://www.eventbrite.com/...' because it violates the following Content Security Policy directive: "frame-src 'self' ..."

Fix

Allow the exact Eventbrite origin used by the embed:

frame-src 'self' https://www.eventbrite.com;

If your integration pulls from another Eventbrite subdomain, add that exact origin after confirming it in DevTools. Don’t jump straight to https://*.eventbrite.com unless you actually need it.

I’m opinionated here: wildcarding third-party domains too early is lazy CSP design. Start narrow, then expand only when the traffic proves you need it.

Mistake #3: Allowing the script, but blocking its network calls

This one wastes a lot of time because the Eventbrite JavaScript loads fine, so people assume CSP is no longer the issue. Then the widget sits there half-rendered because its XHR or fetch() requests are blocked by connect-src.

You’ll usually see errors like:

Refused to connect to 'https://www.eventbrite.com/...' because it violates the following Content Security Policy directive: "connect-src 'self' ..."

Fix

Add Eventbrite to connect-src:

connect-src 'self' https://www.eventbrite.com;

If the embed talks to more than one Eventbrite origin, inspect the Network tab and whitelist the exact endpoints in use.

Starting from the headertest.com example, the diff would look more like this:

Content-Security-Policy:
  default-src 'self' https://www.googletagmanager.com https://*.cookiebot.com https://*.google-analytics.com;
  script-src 'self' 'nonce-YjllN2FlODUtMWExNC00YmNhLWI4MDAtODQyZTRhN2RlMzZk' 'strict-dynamic' https://www.googletagmanager.com https://*.cookiebot.com https://*.google-analytics.com https://www.eventbrite.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 https://www.eventbrite.com;
  frame-src 'self' https://consentcdn.cookiebot.com https://www.eventbrite.com;
  frame-ancestors 'none';
  base-uri 'self';
  form-action 'self' https://www.eventbrite.com;
  object-src 'none'

Mistake #4: Breaking checkout by forgetting form-action

If your Eventbrite integration posts form data to an Eventbrite endpoint, form-action 'self' will block it. This is easy to miss because not every embed path uses a visible <form> you wrote yourself.

Fix

Allow form submissions to Eventbrite:

form-action 'self' https://www.eventbrite.com;

If checkout opens in a popup or redirect instead of a frame, you may also need to review your navigation flow, but form-action is the first thing I check.

Mistake #5: Keeping strict-dynamic but expecting host allowlists to behave normally

The headertest.com policy includes:

script-src 'self' 'nonce-...' 'strict-dynamic' ...

That’s usually a good thing. I like nonce-based CSP with strict-dynamic. But there’s a catch: once you use strict-dynamic, supporting browsers trust nonce-approved scripts and the scripts they load, while host allowlists become less meaningful in practice.

Developers often add Eventbrite to script-src and wonder why behavior still doesn’t line up with their mental model.

Fix

Be deliberate about your script loading strategy.

If you load the Eventbrite script directly in your HTML, nonce it:

<script
  nonce="{{ .CSPNonce }}"
  src="https://www.eventbrite.com/static/widgets/eb_widgets.js">
</script>

And keep your policy like this:

script-src 'self' 'nonce-{RANDOM}' 'strict-dynamic' https://www.eventbrite.com;

That gives you a trusted bootstrap script. From there, any child scripts it loads are allowed in browsers that support strict-dynamic.

If you’re not using nonces correctly, strict-dynamic becomes cargo-cult CSP. Either commit to nonce/hash-based script trust, or simplify until your team can reason about it.

For the spec and browser behavior details, the official reference is the MDN CSP docs: https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP

Mistake #6: Using 'unsafe-inline' for scripts as the quick fix

I get why teams do this. Eventbrite embed snippets often include inline setup code, and the fastest path is:

script-src 'self' 'unsafe-inline' https://www.eventbrite.com;

That works, but it’s a bad trade unless you truly can’t avoid it. You’re giving up one of the biggest XSS protections CSP provides.

Fix

Use a nonce on the inline bootstrap code instead.

Example:

<script
  nonce="{{ .CSPNonce }}"
  src="https://www.eventbrite.com/static/widgets/eb_widgets.js">
</script>

<script nonce="{{ .CSPNonce }}">
  window.EBWidgets.createWidget({
    widgetType: "checkout",
    eventId: "1234567890",
    modal: true,
    modalTriggerElementId: "eventbrite-widget-trigger",
    onOrderComplete: function () {
      console.log("Order complete");
    }
  });
</script>

And the matching policy:

script-src 'self' 'nonce-{RANDOM}' 'strict-dynamic' https://www.eventbrite.com;

That’s much better than turning on 'unsafe-inline'.

Mistake #7: Not running in Report-Only first

When you already have a production CSP with analytics, consent tooling, and other third parties, adding Eventbrite can cause side effects you won’t predict from the docs alone.

Fix

Ship the updated policy in Content-Security-Policy-Report-Only first and inspect the violations.

Example:

Content-Security-Policy-Report-Only:
  default-src 'self';
  script-src 'self' 'nonce-{RANDOM}' 'strict-dynamic' https://www.eventbrite.com;
  frame-src 'self' https://www.eventbrite.com;
  connect-src 'self' https://www.eventbrite.com;
  form-action 'self' https://www.eventbrite.com;
  img-src 'self' data: https:;
  style-src 'self' 'unsafe-inline';
  object-src 'none';
  base-uri 'self';

This is the safest way to discover whether the embed also needs images, additional API origins, or some weird regional endpoint.

The official documentation for CSP headers and reporting is here: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy

A practical Eventbrite CSP template

If I were starting from a reasonably strict app and needed an Eventbrite embed, I’d begin with this:

Content-Security-Policy:
  default-src 'self';
  script-src 'self' 'nonce-{RANDOM}' 'strict-dynamic' https://www.eventbrite.com;
  frame-src 'self' https://www.eventbrite.com;
  connect-src 'self' https://www.eventbrite.com;
  img-src 'self' data: https:;
  style-src 'self' 'unsafe-inline';
  font-src 'self';
  form-action 'self' https://www.eventbrite.com;
  base-uri 'self';
  object-src 'none';
  frame-ancestors 'none';

Then I’d test the actual embed flow and only add more origins if the browser proves they’re required.

That last part matters. CSP gets messy when teams copy-paste allowances they don’t understand. Eventbrite embeds are usually fixable with a handful of targeted changes: script-src, frame-src, connect-src, and sometimes form-action. Most breakage comes from treating CSP like one big allowlist instead of a set of resource-specific rules.

If your embed still fails after these changes, open DevTools, filter for CSP errors, and trust the browser over guesswork. CSP debugging is one of those jobs where the console is usually telling the truth.