Content Security Policy auf Cloudflare
Cloudflare bietet drei verschiedene Wege, einen CSP-Header zu setzen — Transform Rules, Workers und die alten Page Rules — und jeder hat eigene Limits, Kosten und Stolpersteine. Diese Anleitung geht alle drei durch und behandelt anschließend die Cloudflare-spezifischen Fallen (Email Obfuscation, Rocket Loader, Header-Kollisionen mit dem Origin), die eine CSP nach dem Deployment kaputt machen.
Variante 1: Transform Rules (empfohlen für die meisten Seiten)
Transform Rules sind Cloudflares moderner, code-freier Weg, Header an der Edge zu setzen oder umzuschreiben. Sie sind in jedem Tarif inklusive Free verfügbar (mit Quota-Limit) und sind die richtige Default-Wahl für fast jeden CSP-Rollout.
Im Cloudflare-Dashboard die Zone wählen und navigieren zu:
Rules → Transform Rules → Modify Response Header → Create rule
- Rule name:
CSP-Header setzen - When incoming requests match:
All incoming requests(oder per Hostname / URI-Pfad einschränken) - Then… Klicke auf Set static und träge ein:
- Header-Name:
Content-Security-Policy-Report-Only - Wert:
default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'
- Header-Name:
Speichern und deployen. Cloudflare verteilt die Regel innerhalb weniger Sekunden weltweit. Mit curl -I https://deine-seite.example verifizieren — der Header sollte genau einmal auftauchen.
Set vs. Add — die richtige Aktion wählen
Transform Rules bieten zwei Aktions-Typen, und der häufigste Cloudflare-CSP-Bug entsteht dadurch, dass die falsche gewählt wird:
- Set static — ersetzt einen bereits vorhandenen Header gleichen Namens. Verwenden, wenn Cloudflare die einzige Quelle für die CSP sein soll, unabhängig davon was der Origin sendet.
- Add static — fügt einen neuen Header zusätzlich zu dem hinzu, was der Origin schon gesendet hat. Wenn der Origin selbst eine CSP sendet, hat der Browser am Ende zwei CSP-Header — und die werden als Schnittmenge interpretiert (restriktiver als jede einzelne Policy). Das blockiert still und heimlich Sachen, die du nicht erwartest.
Faustregel: Wenn dein Origin gar keine CSP sendet, funktioniert beides. Wenn der Origin schon eine CSP sendet, fast immer Set verwenden.
Free-Plan-Quota
Der Free-Plan begrenzt Transform Rules auf 10 Regeln pro Zone über alle Regel-Typen hinweg (URL Rewrite, Modify Request Header, Modify Response Header). Eine CSP-Regel belegt einen Slot. Wer schon nahe am Limit ist, upgraded entweder auf Pro (50 Regeln) oder weicht auf einen Worker aus.
Variante 2: Cloudflare Workers (wenn Transform Rules nicht reichen)
Ein Worker gibt dir volle programmatische Kontrolle über den Response — das ist Overkill für eine statische CSP, aber nützlich, wenn du Folgendes brauchst:
- Pro-Request rotierende
nonce-Werte für Inline-Scripts (das Strict-CSP-Pattern) - Unterschiedliche Policies abhängig vom Pfad, Cookie oder User-Agent
- Ein
report-urimit request-spezifischem Token
Minimal-Beispiel:
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,
});
},
};
Mit npx wrangler deploy deployen und auf die Zone routen. Workers kosten $5/Monat im Paid-Plan inkl. 10M Requests; der Free-Plan deckt 100k Requests/Tag ab — genügt für kleine Seiten, aber nicht für Production-Traffic.
Variante 3: Page Rules (Legacy — für neue Setups vermeiden)
Ältere Cloudflare-Anleitungen verweisen für Header-Manipulation auf Page Rules, aber Page Rules können keine beliebigen Response-Header setzen — nur spezifische Feature-Toggles wie Cache-TTL und SSL-Mode. Manche Tutorials schlagen vor, Page Rules mit Workers zu kombinieren, was funktioniert, aber unnötig kompliziert ist. Cloudflare selbst migriert Page Rules schrittweise zu Rules-Engine-Äquivalenten — für jeden neuen CSP-Rollout direkt Transform Rules verwenden.
Cloudflare-spezifische Fallen, die CSP brechen
Email Obfuscation injiziert ein Inline-Script
Cloudflares Email Address Obfuscation (Scrape Shield → Email Address Obfuscation) schreibt automatisch mailto:-Links und Email-Texte auf jedem HTML-Response um, um Scrapern den Zugriff zu erschweren. Dazu wird ein Inline-<script>-Block plus ein kleiner externer Loader injiziert — sobald du also eine strikte CSP ohne 'unsafe-inline' in script-src deployst, geht die Obfuskierung kaputt und der Browser loggt bei jedem Seitenaufruf einen CSP-Verstoß.
Zwei Optionen:
- Email Obfuscation deaktivieren — pro Zone unter Scrape Shield. Wenn auf der Seite gar keine
mailto:-Links sind, macht das Feature ohnehin nichts Nützliches. - Cloudflare-Email-Decode-Origin freigeben in der Policy:
script-src 'self' https://ajax.cloudflare.com. Der Inline-Teil braucht aber trotzdem'unsafe-inline'oder eine Nonce — deshalb ist Deaktivieren meist sauberer.
Rocket Loader schreibt jeden Script-Tag um
Rocket Loader (Speed → Optimization) fängt jeden <script>-Tag ab, defert ihn und re-injiziert den Code über Cloudflares eigenen Loader. Der injizierte Loader läuft als Inline-JavaScript mit einer Cloudflare-kontrollierten Hash über https://ajax.cloudflare.com. Mit strikter CSP ist das ein No-Go — Rocket Loader funktioniert grundsätzlich nicht ohne 'unsafe-inline' in script-src.
Wenn du eine strikte CSP willst, deaktiviere Rocket Loader. Wenn du Rocket Loader willst, akzeptiere dass du 'unsafe-inline' https://ajax.cloudflare.com in script-src brauchst — was die Policy deutlich aufweicht.
Origin sendet eigene CSP und Cloudflare hängt eine zweite dran
Wenn dein Origin (Apache, Nginx, WordPress, etc.) bereits eine CSP sendet und deine Transform Rule Add statt Set verwendet, bekommt der Browser zwei Content-Security-Policy-Header und wendet die Schnittmenge an. Typisches Symptom: Sachen die gestern noch funktionierten werden plötzlich blockiert, obwohl du nur "eine Ausnahme hinzugefügt" hast. Verifizieren mit:
curl -I https://deine-seite.example | grep -i content-security-policy
Wenn der Header doppelt erscheint: Transform-Rule-Aktion von Add auf Set umstellen, oder die CSP am Origin entfernen.
Caching: Edge-gecachte Responses übernehmen neue Regeln nicht sofort
Transform Rules werden auf jedem Response angewendet, auch auf solchen die aus Cloudflares Cache kommen. Aber wenn du am Origin aggressive Cache-Settings hast (z.B. Cache-Control: public, max-age=86400) und der Origin in diesen gecachten Responses seine eigene CSP sendet, läuft die Set-Aktion der Transform Rule zwar an der Edge — aber im DevTools kannst du alte Caches sehen wegen Browser-seitigem Caching. Um sauber zu testen: Caching → Configuration → Purge Everything nach Regel-Änderungen, dann aus einem Inkognito-Fenster testen.
HTTP/3 und Header-Reihenfolge
Cloudflare bevorzugt HTTP/3 (QUIC) für Browser-Traffic. HTTP/3 normalisiert Header-Namen auf Kleinbuchstaben und kann sie auf dem Draht umsortieren. Das ist für CSP harmlos — Browser interessieren sich weder für Case noch für Reihenfolge — aber es kann Debug-Tools verwirren, die Header wörtlich darstellen. Mit curl --http2 -I bekommst du eine stabile Sicht, falls die HTTP/3-Ausgabe irreführend ist.
Empfohlene Rollout-Reihenfolge
- CSP generieren. Entweder mit einer generischen Vorlage starten und die Verstoß-Meldungen abarbeiten, oder das Trial-and-Error überspringen und unseren Scanner aus den tatsächlich geladenen Ressourcen eine maßgeschneiderte Policy bauen lassen.
- Email Obfuscation und Rocket Loader deaktivieren für die Zone (oder akzeptieren, dass sie
'unsafe-inline'brauchen). - Transform Rule anlegen mit
Content-Security-Policy-Report-Onlyund der Set static-Aktion. - Browser-Konsole und
report-uri-Endpoint (falls gesetzt) für 1–2 Wochen beobachten. - Header-Namen umbenennen von
Content-Security-Policy-Report-OnlyaufContent-Security-Policyin derselben Regel. - Cache purgen und auf Production aus einer sauberen Browser-Session verifizieren.