Part 3 of the series "EU Web Security: 10 Steps to a Better Rating"
Why CSP Matters
Cross-Site Scripting (XSS) has been the most common web application vulnerability for over 20 years. An attacker injects JavaScript into your website — via a form, a URL parameter, a comment. The visitor's browser executes the code because it cannot distinguish it from legitimate page content.
Content Security Policy (CSP) solves this problem: you explicitly define which sources the browser may accept for scripts, styles, images, and other resources. Everything else is blocked.
Adoption rate in the EU: 10.8%. Nine out of ten websites have no CSP header.
What the Data Shows
From the SiteGuardian Benchmark covering over 700,000 European websites:
- 10.8% have a CSP header
- Websites with security.txt have CSP in 47% of cases — 4.7x more often
- Permissions-Policy (related header): only 6.4%
Deploying CSP Step by Step
The biggest obstacle: an overly strict CSP breaks the site. Google Analytics stops loading, embedded YouTube videos disappear, inline styles no longer work.
Step 1: Report-Only Mode (breaks nothing)
Content-Security-Policy-Report-Only: default-src 'self'; report-uri /csp-report
The browser reports violations but blocks nothing. You see which external sources your site uses without breaking anything.
Step 2: Identify Sources
Monitor the reports for 1-2 weeks. Typical sources you will need to allow:
- Google Analytics:
https://www.googletagmanager.com https://www.google-analytics.com - Google Fonts:
https://fonts.googleapis.com https://fonts.gstatic.com - YouTube Embeds:
https://www.youtube.com https://www.youtube-nocookie.com - Your CDN:
https://cdn.your-domain.com
Step 3: Build the Policy
Content-Security-Policy: default-src 'self'; script-src 'self' https://www.googletagmanager.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src https://fonts.gstatic.com; img-src 'self' data: https:; frame-src https://www.youtube-nocookie.com
Step 4: Activate
Remove -Report-Only from the header name. Test the site thoroughly.
Key Directives
| Directive | Controls | Recommendation |
|---|---|---|
default-src |
Fallback for everything | 'self' |
script-src |
JavaScript | 'self' + explicit domains |
style-src |
CSS | 'self' 'unsafe-inline' (often necessary) |
img-src |
Images | 'self' data: https: |
font-src |
Fonts | 'self' + Google Fonts if needed |
connect-src |
XHR/Fetch | 'self' + API domains |
frame-src |
iframes | Explicit domains only |
object-src |
Flash/Java | 'none' (always) |
base-uri |
base tag | 'self' |
Nginx
add_header Content-Security-Policy "default-src 'self'; script-src 'self' https://www.googletagmanager.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; object-src 'none'; base-uri 'self'" always;
Apache
Header always set Content-Security-Policy "default-src 'self'; script-src 'self' https://www.googletagmanager.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; object-src 'none'; base-uri 'self'"
Common Mistakes
1. unsafe-inline for scripts. script-src 'unsafe-inline' allows any inline script — rendering the entire CSP useless against XSS. Use nonces or hashes instead.
2. * as a source. default-src * allows everything from everywhere. That is not a policy, it is a placebo.
3. Starting too strict. Always begin with Report-Only. A CSP that breaks the site and gets immediately disabled protects no one.
Regulatory Context
- NIS2 Art. 21(2)(d) — security in the development and maintenance of information systems
- GDPR Art. 32 — XSS can lead to data breaches (session hijacking, cookie theft)
- PCI DSS 4.0 — Requirement 6.4.3 explicitly mandates CSP for payment pages
Check Your CSP
SiteGuardian not only detects whether a CSP header exists, but analyses the directives for known weaknesses:
Next week in Part 4: DNSSEC — why 84% of EU domains have no protection against DNS spoofing.