How it works
If an attacker manages to get their own code into your page, say through a comment field or an insecure plugin, that code runs in your visitors' browsers. It can read what they type, take over sessions or swap content. This is called cross-site scripting (XSS).
The Content Security Policy (CSP) is a response header with a list of rules, called directives. script-src says where scripts may come from, img-src does the same for images, and default-src covers everything without a rule of its own. The browser blocks anything the policy does not allow, so injected code does not run.
With Content-Security-Policy-Report-Only the browser only reports violations instead of preventing them. That is meant for testing and gives no protection.
What matters
- Enforce, don't just report. Once the reports in report-only mode are quiet, send the same policy as
Content-Security-Policy. - A rule for scripts. Without
script-srcand withoutdefault-src, scripts are not restricted at all. - No
'unsafe-inline'without a nonce or hash. With it, every script in the HTML runs, including the injected one. A nonce is a random value the server creates afresh for every response and writes into the policy ('nonce-…') and onto each of its own<script>tags. A hash ('sha256-…') allows exactly one known script. If either is in the policy, browsers ignore'unsafe-inline'. - No wildcards for scripts.
*,https:,http:ordata:allow scripts from anywhere. Name the hosts your site needs, or use nonces with'strict-dynamic': allowed scripts may then load others, and host sources no longer count. - No
'unsafe-eval'. It lets text be run as code at runtime, which is exactly what many attacks rely on. object-srcandbase-uri.object-src 'none'prevents embedded plugins (whendefault-srcis missing),base-uri 'self'stops anyone redirecting the page's base address.- Several policies apply together. If the server sends two, content has to pass both.
How to set it up
A starting point you need to adapt to your site:
Content-Security-Policy: default-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'self'
Send it as Content-Security-Policy-Report-Only first and watch the console in the developer tools to see what would be blocked. Then add the sources your site really needs: fonts, analytics, maps, embedded videos. Tag managers and plugins with inline scripts make this laborious; switching to nonces is often the clean way.
The policy is set in the web server or, with nonces, in the application, because they are created for every response. That is usually a job for your agency or developers. Some CMSs have plugins or settings for it. The frame-ancestors directive also protects against clickjacking.