Content Security Policy for JekCMS: A Production-Ready Template

jekcms ships a real, production Content Security Policy — not a strict template, and not a marketing claim. Here's exactly what it blocks, what it deliberately still allows, and why.

Content Security Policy for JekCMS: A Production-Ready Template

jekcms ships a real, production Content Security Policy — not a strict template, and not a marketing claim. Here's exactly what it blocks, what it deliberately still allows, and why.

jekcms's production CSP includes 'unsafe-inline' for both script-src and style-src. This is a deliberate, documented trade-off, not an oversight: the Custom Header Scripts setting lets site owners paste their own tracking/verification snippets directly into the page, which can't be nonce-signed without breaking that feature, and the bundled themes and core templates use inline <script>/<style> in places. Removing unsafe-inline would require rewriting both of those before it could ship — it's an open item, not a hidden gap.

Self-Hosting Fonts Removes Two Directives

Fonts loaded from Google Fonts require two directives: font-src fonts.gstatic.com and style-src fonts.googleapis.com. jekcms's bundled themes, including the default Trends theme, currently load Inter and other typefaces from the Google Fonts CDN rather than self-hosting them — which is why both domains are allowlisted in the CSP. If you switch to self-hosted fonts on your own installation, which we'd recommend for both privacy and one fewer external request, you can drop both domains from your policy and rely on 'self' alone.

Explicit img-src Enumeration

The img-src directive needs to include every external domain your content actually loads images from. A common mistake is setting img-src https: to allow any HTTPS image source — this defeats most of the CSP's value against image-based attacks. jekcms's own default policy takes this shortcut (img-src 'self' data: https:) for compatibility with arbitrary embedded content; if you don't need that flexibility, enumerate your actual image sources explicitly instead: img-src 'self' cdn.yoursite.com data:.

Always Start in Report-Only Mode When You Tighten Further

If you plan to narrow jekcms's default policy for your own site, start with Content-Security-Policy-Report-Only and a report-uri pointing at a collection endpoint before enforcing anything. Collect violations for at least a week — weekend traffic often exercises code paths weekday monitoring misses. jekcms itself does not currently ship a built-in violation-logging endpoint; if you want one, you'll need to point report-uri at a service you control.

jekcms's Actual Production CSP

This is the real header jekcms sends in production (from Security.php), covering the default theme set and the built-in Google Analytics/AdSense/GTM and Facebook Pixel integrations:

Content-Security-Policy:
 default-src 'self';
 base-uri 'self';
 object-src 'none';
 frame-ancestors 'self';
 script-src 'self' 'unsafe-inline'
  https://cdn.jsdelivr.net https://cdnjs.cloudflare.com
  https://www.googletagmanager.com https://www.google-analytics.com
  https://pagead2.googlesyndication.com https://adservice.google.com
  https://www.googleadservices.com https://connect.facebook.net;
 style-src 'self' 'unsafe-inline'
  https://fonts.googleapis.com https://cdn.jsdelivr.net;
 font-src 'self' https://fonts.gstatic.com;
 img-src 'self' data: https:;
 frame-src 'self' https://googleads.g.doubleclick.net
  https://tpc.googlesyndication.com https://www.google.com
  https://www.facebook.com;
 connect-src 'self' https://www.google-analytics.com
  https://analytics.google.com https://www.googletagmanager.com
  https://pagead2.googlesyndication.com https://adservice.google.com
  https://www.facebook.com;

Notice what's not there: 'unsafe-eval'. None of jekcms's own code or its bundled third-party scripts (Tiptap, Google Analytics/AdSense/GTM, Facebook Pixel) require it, so it was never added to the script-src allowlist.

Directive-by-Directive Explanation

  • default-src 'self' — baseline for any resource type not explicitly listed; restricts everything to the same origin
  • object-src 'none' — blocks Flash, Java applets, and other plugin content; common XSS vectors with no modern use
  • base-uri 'self' — prevents attackers from injecting a base tag to redirect relative URLs to a malicious domain
  • frame-ancestors 'self' — equivalent to X-Frame-Options: SAMEORIGIN but with broader browser support; prevents clickjacking (jekcms sends both headers)

Why the AdSense and Analytics Domains Are There

The googlesyndication.com, adservice.google.com, googleadservices.com, and doubleclick.net domains in the policy above exist because jekcms's built-in Google integration plugin injects AdSense, GA4, and GTM snippets when you configure them in settings — not because every jekcms site runs ads. If you never enable AdSense on your installation, you can safely remove those domains from a customised policy. Google's ad infrastructure rotates and adds subdomains over time, so if you tighten the policy yourself, expect to revisit it occasionally rather than treating any allowlist as permanent.

Common Mistakes When Customising the Policy

  • Adding unsafe-eval because a library uses eval() — find an alternative library instead; jekcms's own stack doesn't need it
  • Whitelisting CDN domains broadly (e.g., *.cloudflare.com) — attackers can host scripts on the same CDN
  • Forgetting connect-src for AJAX requests — breaks admin panel functionality silently
  • Testing only the public frontend — the admin panel loads a different script set (the editor bundle, media picker) and needs its own check

Where This Stands Today, Honestly

jekcms applies one global CSP in production via Security.php — there's no separate, more permissive policy for /admin/ routes, and no nonce-based script loading yet. The code has an explicit TODO marking nonce migration as a larger refactor: it would require updating every inline script across the admin panel and bundled themes before 'unsafe-inline' could be dropped from script-src. There's also no built-in CSP violation logging to an admin-visible table — if you want to monitor violations, point report-uri at your own collection endpoint. None of this is hidden; it's a documented, load-bearing trade-off between security headroom and not breaking the Custom Header Scripts feature or existing themes.

Order Today

One-time payment, lifetime access. Setup in 30 minutes.

View Pricing
  • Setup and live in 30 minutes
  • 14+ professional themes
  • n8n automation integration
  • Automatic SEO — Sitemap, Schema.org
  • iyzico payment support

Be the first to know

New features, release notes & CMS guides — a couple of emails a month, no spam.