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-srcfor loading Evergreen JavaScriptconnect-srcfor API calls, event delivery, and realtime/streaming endpointsimg-srcif it tracks via image beacons or renders remote assetsstyle-srcif injected UI or hosted styles are involvedframe-srcif 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-dynamicis worth using if your bootstrapped script loads other trusted scripts.- The host source like
https://cdn.segment.comstill helps with backward compatibility in browsers that don’t fully lean onstrict-dynamic. - I avoid
'unsafe-inline'inscript-srcunless 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-srcis not doing all the workscript-srcis nonce-basedconnect-srcis where a lot of vendor complexity landsframe-ancestors,base-uri, andobject-srcare 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 nonsensebase-uri 'self'stops malicious base URL injection tricksform-action 'self'prevents form posts to arbitrary originsframe-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.
Recommended rollout path
My default rollout sequence:
- Start with
Content-Security-Policy-Report-Only - Add only the Evergreen sources you actually hit
- Prefer exact hosts over wildcards
- Keep nonce-based
script-srcif your app already has it - 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.