Missing Content-Security-Policy

What it is

Your site does not send the Content-Security-Policy header. This header tells the browser which sources it may load scripts, images, fonts and iframes from.

Without it, the browser runs any code that appears in the HTML — whether your team put it there or an attacker did via XSS.

Why it’s a problem

CSP is the only effective defense against cross-site scripting (XSS). When an attacker finds a hole somewhere in the application and pushes a <script> through it, without CSP the browser happily runs that script. With CSP the browser says “this source isn’t on my allowlist” and the script fails.

The second dimension is protection against injected drainers and SEO spam. If you have script-src set up correctly, an attacker who compromises a plugin can’t easily add an external script.

How to fix it

CSP is a policy that can break the page. Roll it out in report-only mode first.

Phase 1 — report-only

Content-Security-Policy-Report-Only: default-src 'self'; img-src 'self' data: https:; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; report-uri /csp-report

This policy blocks nothing — it just sends JSON to /csp-report on every violation. Watch the logs for a few days so you know which scripts/images/fonts the page actually uses.

Phase 2 — tune the allowlist

Build a concrete allowlist from the reports. Example for WordPress with Google Fonts and Umami analytics:

Content-Security-Policy:
  default-src 'self';
  script-src 'self' https://i.xhs.cz;
  style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;
  font-src 'self' https://fonts.gstatic.com;
  img-src 'self' data: https://*.gravatar.com;
  connect-src 'self' https://i.xhs.cz;
  frame-ancestors 'none';
  base-uri 'self';
  form-action 'self';

Phase 3 — switch to enforce

Once report-only reports no legitimate violations, change the header from Content-Security-Policy-Report-Only to Content-Security-Policy.

Watch out for 'unsafe-inline'

Inline scripts (<script>...</script>) and inline event handlers (onclick="...") require 'unsafe-inline'. But that strips CSP of most of its value against XSS. It’s better to use a nonce or a hash and phase out inline scripts. WordPress has built-in support for nonces.

Concrete implementations

References