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-reportThis 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
- nginx:
add_header Content-Security-Policy "..." always; - Apache:
Header always set Content-Security-Policy "..." - WordPress plugin: HTTP Headers