securityaudit.website
Fix guide

How to Fix a Missing Content-Security-Policy Header

If your audit flagged a missing Content-Security-Policy, you're in good company — it's the single most common serious gap we see. CSP is also the strongest defence you have against cross-site scripting (XSS), so it's worth getting right.

What CSP actually does

A Content-Security-Policy header tells the browser which sources of content are allowed to load and run on your page. If an attacker manages to inject a <script> tag, a good CSP simply refuses to execute it — the injection becomes inert. Without CSP, the browser trusts whatever ends up in the DOM.

Don't enforce on day one

The mistake that scares people away from CSP is shipping a strict policy straight to production and watching half the site stop working. Avoid that. Start in report-only mode, which logs violations without blocking anything:

Content-Security-Policy-Report-Only: default-src 'self'; report-uri /csp-report

Let that run for a few days, collect the violation reports, and you'll see exactly which third-party sources (analytics, fonts, CDNs) your site legitimately needs.

A sane starting policy

Once you know your real dependencies, enforce a policy built around them. A reasonable baseline for a typical site:

Content-Security-Policy:
  default-src 'self';
  script-src 'self';
  style-src 'self' 'unsafe-inline';
  img-src 'self' data:;
  object-src 'none';
  base-uri 'self';
  frame-ancestors 'none'

A few notes:

  • object-src 'none' and base-uri 'self' close off two commonly forgotten injection vectors.
  • frame-ancestors 'none' also handles clickjacking, so you may not need a separate X-Frame-Options header.
  • Avoid 'unsafe-inline' on script-src. If you have inline scripts, move them to files or use a nonce.

Using nonces for inline scripts

If you can't remove inline scripts, generate a random nonce per request and reference it:

Content-Security-Policy: script-src 'self' 'nonce-r4nd0m';
<script nonce="r4nd0m">/* allowed */</script>

The browser only runs inline scripts carrying the matching nonce, so injected scripts — which won't know the nonce — are blocked.

Where to set it

Set the header at your web server or framework so it applies everywhere:

# nginx
add_header Content-Security-Policy "default-src 'self'; object-src 'none'; base-uri 'self'" always;
# Apache
Header always set Content-Security-Policy "default-src 'self'; object-src 'none'; base-uri 'self'"

Verify

Re-run your audit after deploying. A passing result means CSP is present without 'unsafe-inline' or 'unsafe-eval' on scripts — the configuration that actually stops XSS rather than just looking like it does.

Want to know if your site has this issue?
Run a free audit →

More guides