Evergreen by Segment is one of those tools that quietly expands your CSP over time if you’re not paying attention. You start with a neat script-src, then analytics, edge APIs, previews, and embedded UI bits show up and your policy turns into a junk drawer.

This guide is the version I wish I had when wiring CSP around Evergreen on a production site.

I’m assuming you want copy-paste examples first, with enough explanation to avoid breaking your app.

What Evergreen usually needs

For a typical Evergreen integration, you’ll usually care about:

  • script-src for loading Evergreen JavaScript
  • connect-src for API calls, event delivery, and realtime/streaming endpoints
  • img-src if it tracks via image beacons or renders remote assets
  • style-src if injected UI or hosted styles are involved
  • frame-src if Evergreen opens embedded editors, previews, or hosted admin surfaces

If you’re already running a strict CSP with nonces and strict-dynamic, you’ll want the Evergreen hostnames added in the right places without blowing a hole in the policy.

Start with Report-Only first

Do this before enforcing anything:

Content-Security-Policy-Report-Only: default-src 'self'; script-src 'self'; connect-src 'self'; img-src 'self' data:; style-src 'self'; font-src 'self'; frame-src 'self'; object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'none'

Then load your app, exercise Evergreen features, and inspect violations.

If you need a refresher on directives, https://csp-guide.com is a solid quick reference.

Minimal CSP example for Evergreen

If you just need a starting point, this is the smallest policy shape I’d begin with and then tighten from actual violations:

Content-Security-Policy:
  default-src 'self';
  script-src 'self' https://cdn.segment.com;
  connect-src 'self' https://api.segment.io https://cdn.segment.com;
  img-src 'self' data: https:;
  style-src 'self' 'unsafe-inline';
  font-src 'self' https://cdn.segment.com;
  frame-src 'self';
  object-src 'none';
  base-uri 'self';
  form-action 'self';
  frame-ancestors 'none'

That’s deliberately conservative, but not “strict” in the CSP-hardening sense.

Stricter nonce-based CSP for Evergreen

If your app already uses nonces, keep doing that. Don’t fall back to 'unsafe-inline' in script-src just because a vendor asks nicely.

Example:

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

A few notes:

  • strict-dynamic is worth using if your bootstrapped script loads other trusted scripts.
  • The host source like https://cdn.segment.com still helps with backward compatibility in browsers that don’t fully lean on strict-dynamic.
  • I avoid 'unsafe-inline' in script-src unless there is absolutely no alternative.

Real-world policy shape

Here’s a real CSP header from headertest.com, which is a good example of what modern production policies often look like when multiple third parties are involved:

content-security-policy:
default-src 'self' https://www.googletagmanager.com https://*.cookiebot.com https://*.google-analytics.com;
script-src 'self' 'nonce-MzJjNGI2YTYtN2E1YS00ZDdkLWFhNjEtNGM5NGY2MDdmMzRk' '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'

The useful takeaway isn’t the exact domains. It’s the structure:

  • default-src is not doing all the work
  • script-src is nonce-based
  • connect-src is where a lot of vendor complexity lands
  • frame-ancestors, base-uri, and object-src are locked down

That’s the model I’d use for Evergreen too.

Copy-paste policies by setup

1. Basic Evergreen setup

Content-Security-Policy:
  default-src 'self';
  script-src 'self' https://cdn.segment.com;
  connect-src 'self' https://api.segment.io;
  img-src 'self' data: https:;
  style-src 'self' 'unsafe-inline';
  font-src 'self';
  frame-src 'self';
  object-src 'none';
  base-uri 'self';
  form-action 'self';
  frame-ancestors 'none'

2. Evergreen with nonce-based app scripts

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

3. Evergreen with WebSocket support

Some products sneak in realtime features later. If Evergreen uses WebSockets in your setup, add wss: or the exact host:

Content-Security-Policy:
  default-src 'self';
  script-src 'self' 'nonce-{RANDOM_NONCE}' 'strict-dynamic' https://cdn.segment.com;
  connect-src 'self' https://api.segment.io wss://api.segment.io;
  img-src 'self' data: https:;
  style-src 'self' 'unsafe-inline';
  font-src 'self';
  frame-src 'self';
  object-src 'none';
  base-uri 'self';
  form-action 'self';
  frame-ancestors 'none'

I strongly prefer exact wss://host values over broad wss: unless you really need the wildcard behavior.

4. Evergreen embedded/admin UI case

If your integration opens hosted UI in an iframe:

Content-Security-Policy:
  default-src 'self';
  script-src 'self' 'nonce-{RANDOM_NONCE}' 'strict-dynamic' https://cdn.segment.com;
  connect-src 'self' https://api.segment.io;
  img-src 'self' data: https:;
  style-src 'self' 'unsafe-inline';
  font-src 'self';
  frame-src 'self' https://app.segment.com;
  object-src 'none';
  base-uri 'self';
  form-action 'self';
  frame-ancestors 'none'

Only add frame-src hosts if you actually embed them.

Example server configs

Nginx

add_header Content-Security-Policy "
  default-src 'self';
  script-src 'self' 'nonce-$request_id' 'strict-dynamic' https://cdn.segment.com;
  connect-src 'self' https://api.segment.io;
  img-src 'self' data: https:;
  style-src 'self' 'unsafe-inline';
  font-src 'self';
  frame-src 'self';
  object-src 'none';
  base-uri 'self';
  form-action 'self';
  frame-ancestors 'none';
" always;

Fair warning: $request_id is not a proper CSP nonce unless you control its format and inject the exact same value into script tags for that response. People copy examples like this and then wonder why the browser still blocks scripts.

Express / Node.js

app.use((req, res, next) => {
  const nonce = crypto.randomUUID();

  res.locals.cspNonce = nonce;

  res.setHeader(
    "Content-Security-Policy",
    [
      "default-src 'self'",
      `script-src 'self' 'nonce-${nonce}' 'strict-dynamic' https://cdn.segment.com`,
      "connect-src 'self' https://api.segment.io",
      "img-src 'self' data: https:",
      "style-src 'self' 'unsafe-inline'",
      "font-src 'self'",
      "frame-src 'self'",
      "object-src 'none'",
      "base-uri 'self'",
      "form-action 'self'",
      "frame-ancestors 'none'",
    ].join("; ")
  );

  next();
});

Then in your template:

<script nonce="{{cspNonce}}">
  window.appConfig = { evergreen: true };
</script>
<script nonce="{{cspNonce}}" src="/app.js"></script>

Common Evergreen CSP violations

Refused to load script

Browser error usually looks like:

Refused to load the script 'https://cdn.segment.com/...' because it violates the following Content Security Policy directive: "script-src ..."

Fix:

  • add the exact host to script-src
  • if it’s loaded by a nonce-approved bootstrap script, make sure your nonce is valid and consistent
  • if using strict-dynamic, remember older browser behavior may still require host allowlisting

Refused to connect

Usually this means connect-src is incomplete.

Refused to connect to 'https://api.segment.io/...' because it violates the following Content Security Policy directive: "connect-src 'self' ..."

Fix:

connect-src 'self' https://api.segment.io

If you see WebSocket errors, add the exact wss:// endpoint too.

Refused to frame

If Evergreen opens embedded content:

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

Fix:

frame-src 'self' https://app.segment.com

Directives I would keep locked down

These aren’t Evergreen-specific, but I almost always include them:

object-src 'none';
base-uri 'self';
form-action 'self';
frame-ancestors 'none'

Why:

  • object-src 'none' shuts off a pile of legacy nonsense
  • base-uri 'self' stops malicious base URL injection tricks
  • form-action 'self' prevents form posts to arbitrary origins
  • frame-ancestors 'none' blocks clickjacking unless you intentionally allow embedding

If your site must be embedded by a trusted parent, replace frame-ancestors 'none' with the exact allowed origins.

What not to do

I’ve seen all of these in real deployments:

Don’t do this

script-src * 'unsafe-inline' 'unsafe-eval';
connect-src *;

That’s barely a policy. It makes auditors happy and your browser sad.

Don’t dump third-party domains into default-src

Be explicit instead:

default-src 'self';
script-src 'self' https://cdn.segment.com;
connect-src 'self' https://api.segment.io;

default-src fallback behavior is useful, but relying on it heavily makes debugging worse.

Don’t wildcard everything

Avoid this unless you truly need it:

connect-src 'self' https://*.segment.com;

Better:

connect-src 'self' https://api.segment.io

Start exact. Broaden only when the product proves it needs more.

My default rollout sequence:

  1. Start with Content-Security-Policy-Report-Only
  2. Add only the Evergreen sources you actually hit
  3. Prefer exact hosts over wildcards
  4. Keep nonce-based script-src if your app already has it
  5. Enforce after you’ve tested login, dashboard flows, embedded views, and error paths

That last one matters. Third-party tools often behave differently in authenticated areas than in public pages.

Official documentation

For vendor-specific hostnames and current integration details, check Segment’s official documentation.

For CSP directive behavior and examples, see:

If you want the shortest practical answer: start with a nonce-based policy, add Evergreen hosts to script-src and connect-src, and resist the urge to “fix” violations with wildcards. That shortcut always comes back to bite later.