Cloudflare installation
How to allow the Meridian Blue loader through a Cloudflare-managed Content Security Policy.
Cloudflare installation
If your site sits behind Cloudflare and your browser console shows:
Refused to load the script 'https://cdn.meridianblue.ai/v1/loader.js' because it
violates the following Content Security Policy directive: "script-src 'self' ..."
…then your site is enforcing a Content Security Policy (CSP) that does not yet allowlist our one script URL. This is the browser doing its job — CSP cannot be bypassed by us, only by you, and it takes about 90 seconds in the Cloudflare dashboard.
You only ever need to allowlist two hostnames:
| Hostname | Used for | Goes in |
|---|---|---|
https://cdn.meridianblue.ai | The loader + runtime bundle | script-src |
https://plugin.meridianblue.ai | Config fetch + event ingestion | connect-src |
That is the entire installation. No inline scripts, no multiple URLs, no vendor spaghetti.
Step 1 — Find your current CSP header
Open your site in Chrome or Firefox, open DevTools → Network, reload the page, and click the top document request. Under Response Headers look for:
content-security-policy, orcontent-security-policy-report-only
Copy the full value. You will edit this string in step 3. If there is no
header at all, you are not enforcing CSP and the loader should already work —
if it still fails, check that you are not serving a <meta http-equiv="Content-Security-Policy">
tag from your HTML (grep the page source).
Step 2 — Open Cloudflare Transform Rules
- Sign in to dash.cloudflare.com.
- Pick the zone (the domain) you want to install on.
- In the left sidebar, go to Rules → Transform Rules.
- Click the Modify Response Header tab at the top.
- Click Create rule.
If your plan does not show "Modify Response Header", skip to the Cloudflare Workers fallback at the bottom of this page.
Step 3 — Create the rule
Fill in the form exactly as below.
Rule name
Meridian Blue — allow CDN in CSP
If — Custom filter expression → Edit expression
Match every HTML response on your site:
(http.response.content_type.media_type eq "text/html")
If you want to scope it to a single hostname, append:
and (http.host eq "www.example.com")
Then — Modify response header
- Action:
Set dynamic - Header name:
Content-Security-Policy - Value (expression):
concat(
regex_replace(
regex_replace(http.response.headers["content-security-policy"][0],
"script-src([^;]*)",
"script-src$1 https://cdn.meridianblue.ai"),
"connect-src([^;]*)",
"connect-src$1 https://cdn.meridianblue.ai https://plugin.meridianblue.ai")
)
This takes whatever CSP your origin is already sending and splices our two
hostnames into the existing script-src and connect-src directives, without
touching anything else. If one of those directives does not exist yet, use the
manual version instead — see step 3b.
Step 3b — Manual CSP value (if you don't have one yet)
If your origin sends no CSP header, paste a complete, strict-but-working baseline instead. Set Action → Set static (not dynamic) and use:
default-src 'self';
script-src 'self' https://cdn.meridianblue.ai;
connect-src 'self' https://cdn.meridianblue.ai https://plugin.meridianblue.ai;
img-src 'self' data: https:;
style-src 'self' 'unsafe-inline';
font-src 'self' data:;
frame-ancestors 'self';
base-uri 'self';
object-src 'none';
Only use this baseline if you know your site does not depend on other inline scripts, fonts, or third-party widgets. When in doubt, use the dynamic version in step 3 — it preserves your current policy.
Step 4 — Deploy and verify
- Click Deploy in Cloudflare. The rule is live globally within seconds.
- Hard-reload your site (
⌘⇧R/Ctrl+F5) so the browser discards any cached CSP header. - Open DevTools → Network and look for
loader.js:- Status should be 200, not (blocked:csp).
- The Console tab should have no red CSP errors.
- Open DevTools → Application → Storage → Local storage and confirm a
mb_prefixed key was written — that's the SDK booting successfully.
If loader.js is 200 but you still see a CSP error for an mb_ URL (e.g.
plugin.meridianblue.ai/v1/config/…), you forgot connect-src. Repeat
step 3.
Step 5 — (Optional) lock it down
Our loader never evaluates inline scripts, never calls eval, and never
reaches outside the two hostnames above. That means you can safely drop
'unsafe-inline' and 'unsafe-eval' from your script-src — as long as
nothing else on your site needs them. Doing so upgrades your CSP from
"advisory" to "real protection".
You do not need to add us to:
img-src(we render no images from our own origin)font-src(we use only system fonts)frame-src/child-src(we do not iframe anything)style-src(all styles live inside a closed Shadow DOM root)
That is the whole point of the shadow-DOM isolation: your site's CSS and ours can never fight, and your CSP stays as tight as you want it.
Fallback: Cloudflare Workers
If your Cloudflare plan does not include Modify Response Header (very old Free plans), you can do the same thing with a tiny Worker.
- Go to Workers & Pages → Create application → Create Worker.
- Name it
meridianblue-cspand replace the default code with:
export default {
async fetch(request, env, ctx) {
const response = await fetch(request);
// Only touch HTML responses — don't rewrite headers on images, etc.
const ct = response.headers.get("content-type") || "";
if (!ct.includes("text/html")) return response;
const headers = new Headers(response.headers);
const existing = headers.get("content-security-policy") || "";
const patched = existing
? existing
.replace(/script-src([^;]*)/, "script-src$1 https://cdn.meridianblue.ai")
.replace(/connect-src([^;]*)/, "connect-src$1 https://cdn.meridianblue.ai https://plugin.meridianblue.ai")
: "default-src 'self'; script-src 'self' https://cdn.meridianblue.ai; connect-src 'self' https://cdn.meridianblue.ai https://plugin.meridianblue.ai; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:;";
headers.set("content-security-policy", patched);
return new Response(response.body, {
status: response.status,
statusText: response.statusText,
headers,
});
},
};
- Click Save and Deploy.
- Go to your zone → Workers Routes → Add route and bind the Worker to
*your-domain.com/*.
Troubleshooting
"I deployed the rule but still see the CSP error." Hard-reload. Browsers cache CSP headers aggressively. If that doesn't work, check Cloudflare → Caching → Configuration → Development Mode is off and purge the HTML page from the cache.
"My origin sends CSP via a <meta> tag, not a header."
Cloudflare Transform Rules cannot edit HTML body content — only headers. You
will need to either (a) remove the <meta> and switch to the header approach
above, or (b) patch the tag at the application layer (WordPress theme,
Next.js middleware, etc.). Open an issue if you're stuck.
"I have CSP in report-only mode and want to test first."
Change the header name in step 3 from Content-Security-Policy to
Content-Security-Policy-Report-Only. The browser will log violations to
your report endpoint instead of blocking the script.
"I have a nonce-based CSP (script-src 'nonce-…')."
The regex rewrite in step 3 still works — it appends our hostname alongside
the nonce. You do not need to give us a nonce; we are loaded as an
external script, not inline.
Why this is the only configuration step
Every other option is worse:
- Inlining the SDK into every page bloats your HTML by ~25KB on every
request, defeats browser caching, breaks CSP
script-srctightening (you'd need'unsafe-inline'or per-page nonces), and makes upgrades a nightmare — every customer would be frozen on whatever version they pasted. - Same-origin proxy (you serve our JS from
yourdomain.com/mb/loader.js) works but gives up caching, shifts our CDN cost to your origin, and still requires an edit to your infrastructure — we picked Cloudflare because it's the fastest place to make the fix. - A browser extension only works for you, not your visitors.
Allowlisting two hostnames in one header is the smallest possible change that keeps CSP actually enforced. That's why it is the supported install path.