Security Header Guide

Content-Security-Policy

Tells browsers exactly which sources of scripts, images, fonts, and other content are allowed to load on your site — blocking everything else, including injected malicious code.


What it does in plain English

Cross-Site Scripting (XSS) is one of the most common web attacks. An attacker finds a way to inject malicious JavaScript into your page — through a comment form, a URL parameter, or a compromised third-party script — and that code runs in your visitors' browsers, stealing passwords or session cookies.

CSP is a whitelist you give to the browser: "Only run scripts from my own domain and from cdn.mytrusteddomain.com. Block everything else — even if it somehow ends up on my page." It's the last line of defence against XSS.

Real-world analogy

Think of CSP as a strict guest list for a private event. Your site specifies exactly who's allowed in. Even if someone sneaks a stranger's name onto a sign-up sheet (injects a script), security at the door (the browser) checks against the official list and turns them away.

Good vs. Bad examples

❌ Missing — No Protection

No CSP header

Without CSP, any JavaScript that ends up on your page — injected by an attacker, loaded by a compromised third-party script, or hidden in user-submitted content — will execute freely in your visitors' browsers.

⚠ Present but Weak
Content-Security-Policy: script-src 'self' 'unsafe-inline' 'unsafe-eval'

'unsafe-inline' allows inline <script> tags and onclick handlers. 'unsafe-eval' allows eval(). Both are common in older codebases but they largely defeat XSS protection — an injected inline script will still run.

✓ Strong Policy
Content-Security-Policy: default-src 'self'; script-src 'self' 'nonce-{randomNonce}'; style-src 'self'; img-src 'self' data:; object-src 'none'; base-uri 'none'

Only allows scripts with a matching server-generated nonce. No inline scripts, no eval, no content from other origins. object-src 'none' and base-uri 'none' close additional injection vectors.



How to fix it

CSP is the most complex header to configure correctly because it depends on exactly which third-party resources your site loads. Start in report-only mode to catch issues without breaking anything.

  1. Start with Content-Security-Policy-Report-Only instead of Content-Security-Policy. The policy is enforced in shadow mode — violations are logged but nothing breaks.
  2. Check your browser console for CSP violations. Add any legitimate sources to your allowlist.
  3. Once violations stop, switch to the enforcing Content-Security-Policy header.
  4. If you use Google Analytics, Tag Manager, or Stripe, add their domains explicitly rather than using 'unsafe-inline'.

Nginx

add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; object-src 'none';" always;

Apache

Header always set Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; object-src 'none';"

Cloudflare (Transform Rules)

In Cloudflare dashboard → Rules → Transform Rules → Modify Response Header. Add a static header with your CSP value. No server access required.

⚠ Note for sites using page builders or plugins

WordPress page builders, chat widgets, and analytics tools often inject inline scripts that will break under a strict CSP. Use report-only mode first, and expect to spend time tuning your policy around third-party tools.

← Back to Security Header Grader