EN DE

Content Security Policy on Cloudflare

Cloudflare gives you three different ways to set a CSP header — Transform Rules, Workers and the legacy Page Rules — and each has different limits, costs and edge cases. This guide walks through all three, then covers the Cloudflare-specific pitfalls (Email Obfuscation, Rocket Loader, header collisions with the origin) that break a CSP after you deploy it.

Option 1: Transform Rules (recommended for most sites)

Transform Rules are Cloudflare's modern, zero-code way to add or rewrite headers at the edge. They're available on every plan including Free (with quota limits), and they're the right default for almost every CSP rollout.

In the Cloudflare dashboard, select your zone and navigate to:

Rules → Transform Rules → Modify Response Header → Create rule

Save and deploy. Cloudflare propagates the rule globally in a few seconds. Verify with curl -I https://your-site.example — the header should show up exactly once.

Set vs. Add — pick the right action

Transform Rules give you two action types and getting them wrong is the most common Cloudflare CSP bug:

Rule of thumb: if your origin sends no CSP at all, either action works. If the origin does send a CSP, almost always use Set.

Free plan quota

The Free plan caps Transform Rules at 10 rules per zone across all rule types (URL Rewrite, Modify Request Header, Modify Response Header). One CSP rule is one slot. If you're already near the limit, either upgrade to Pro (50 rules) or fall back to a Worker.

Option 2: Cloudflare Workers (when Transform Rules aren't enough)

A Worker gives you full programmatic control over the response, which is overkill for a static CSP but useful if you need to:

Minimal example:

export default {
  async fetch(request, env, ctx) {
    const response = await fetch(request);
    const headers = new Headers(response.headers);

    headers.set(
      'Content-Security-Policy-Report-Only',
      "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'"
    );

    return new Response(response.body, {
      status:     response.status,
      statusText: response.statusText,
      headers,
    });
  },
};

Deploy with npx wrangler deploy and route it to your zone. Workers cost $5/month for the Paid plan with 10M requests included; the Free plan covers 100k requests/day, which is enough for small sites but not for production traffic.

Skip the trial and error: our scanner crawls your site and gives you the exact CSP you need, with all required origins already whitelisted. Copy-paste into the Transform Rule, done.

Option 3: Page Rules (legacy — avoid for new setups)

Older Cloudflare guides reference Page Rules for header manipulation, but Page Rules cannot set arbitrary response headers — only specific feature toggles like cache TTL and SSL mode. Some tutorials suggest combining Page Rules with Workers to set headers, which works but is needlessly complicated. Cloudflare itself is in the process of migrating Page Rules to Rules-engine equivalents, so for any new CSP rollout use Transform Rules directly.

Cloudflare-specific pitfalls that break CSP

Email Obfuscation injects an inline script

Cloudflare's Email Address Obfuscation feature (Scrape Shield → Email Address Obfuscation) automatically rewrites mailto: links and email text on every HTML response to defeat scrapers. The way it does this is by injecting an inline <script> block plus a tiny external loader — so the moment you deploy a strict CSP without 'unsafe-inline' in script-src, the obfuscation breaks and the browser logs a CSP violation for every page load.

Two options:

Rocket Loader rewrites every script tag

Rocket Loader (Speed → Optimization) intercepts every <script> tag, defers it, and re-injects the code through Cloudflare's own loader. The injected loader runs from https://ajax.cloudflare.com as inline JavaScript with a Cloudflare-controlled hash. With strict CSP this is a non-starter — Rocket Loader fundamentally cannot work without 'unsafe-inline' in script-src.

If you want a strict CSP, disable Rocket Loader. If you want Rocket Loader, accept that you'll need 'unsafe-inline' https://ajax.cloudflare.com in script-src, which substantially weakens the policy.

Origin sends its own CSP and Cloudflare appends a second one

If your origin (Apache, Nginx, WordPress, etc.) already sends a CSP and your Transform Rule uses Add instead of Set, the browser receives two Content-Security-Policy headers and applies their intersection. Common symptom: things that worked yesterday suddenly get blocked, even though you only "added an exception". Verify with:

curl -I https://your-site.example | grep -i content-security-policy

If you see the header twice, change the Transform Rule action from Add to Set, or strip the origin's CSP at the origin.

Caching: edge-cached responses don't pick up new rules instantly

Transform Rules apply on every response, including ones served from Cloudflare's cache. But if you have aggressive cache settings on the origin (e.g. Cache-Control: public, max-age=86400) and the origin sends its own CSP header in those cached responses, the Transform Rule's Set action still runs at the edge — but you might see old caches in DevTools because of browser-side caching. To force a clean test, do Caching → Configuration → Purge Everything after rule changes and test from an incognito window.

HTTP/3 and header order

Cloudflare prefers HTTP/3 (QUIC) for browser traffic. HTTP/3 normalises header names to lowercase and may reorder them on the wire. This is harmless for CSP — browsers don't care about case or order — but it can confuse debugging tools that show headers verbatim. Use curl --http2 -I to get a stable view if HTTP/3 output is misleading.

Recommended rollout order

  1. Generate your CSP. Either start from a generic template and watch the violations roll in, or skip the trial and error and let our scanner build a tailored policy from your actual loaded resources.
  2. Disable Email Obfuscation and Rocket Loader for the zone (or accept they need 'unsafe-inline').
  3. Create the Transform Rule with Content-Security-Policy-Report-Only and the Set static action.
  4. Monitor the browser console and your report-uri endpoint (if you set one) for 1–2 weeks.
  5. Switch the header name from Content-Security-Policy-Report-Only to Content-Security-Policy in the same rule.
  6. Purge cache and verify on production from a clean browser session.
Skip the guesswork — scan your site → Get the exact CSP for your Cloudflare Transform Rule in 30 seconds, with every external resource your site loads already whitelisted