What Is Content Security Policy?
Content Security Policy (CSP) is an HTTP response header that tells the browser which sources of content — scripts, styles, images, fonts, frames — are considered trustworthy for a given page. Any resource that doesn't match the policy is blocked before it executes.
CSP is your primary defense against Cross-Site Scripting (XSS) attacks. Even if an attacker injects a script tag into your page, a properly configured CSP will prevent the browser from executing it.
How CSP Works
You deliver a CSP via the Content-Security-Policy HTTP header (or a <meta> tag, though the header is strongly preferred). The browser reads the policy and enforces it for every subresource loaded on the page.
Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.example.com; img-src *;
This policy says: load everything from the same origin by default, allow scripts from the same origin and one CDN, and allow images from anywhere.
Key Directives Explained
| Directive | Controls |
|---|---|
default-src | Fallback for all resource types not explicitly listed |
script-src | JavaScript sources |
style-src | CSS sources |
img-src | Image sources |
connect-src | Fetch, XHR, WebSocket endpoints |
font-src | Font file sources |
frame-src | Allowed iframe sources |
form-action | Where forms can be submitted |
upgrade-insecure-requests | Automatically upgrade HTTP to HTTPS |
The Report-Only Mode: Your Best Friend
Rolling out CSP on an existing site is notoriously tricky — one wrong directive breaks legitimate functionality. The solution is Content-Security-Policy-Report-Only:
Content-Security-Policy-Report-Only: default-src 'self'; report-uri /csp-report-endpoint
In report-only mode, the browser simulates the policy and sends violation reports to your endpoint without actually blocking anything. You can collect violations for days or weeks, then tune your policy before enforcing it.
Nonces and Hashes: Moving Beyond 'unsafe-inline'
Many developers default to 'unsafe-inline' to allow inline scripts, which defeats much of CSP's purpose. The modern approach uses nonces or hashes:
- Nonce: Your server generates a cryptographically random value per request, adds it to the CSP header, and adds a matching
nonceattribute to legitimate script tags. Only scripts with the matching nonce execute. - Hash: Compute the SHA-256 hash of an inline script and include it in the CSP. The browser computes and compares the hash before executing.
Content-Security-Policy: script-src 'nonce-r4nd0mV4lu3=='
<script nonce="r4nd0mV4lu3==">/* legitimate code */</script>
Common Mistakes to Avoid
- Using
'unsafe-eval'to alloweval()— this re-opens the door to many attacks. - Wildcard sources like
script-src *— this is equivalent to having no policy. - Forgetting
frame-ancestorsto prevent clickjacking (use it alongside theX-Frame-Optionsheader). - Not testing your policy in report-only mode before enforcing.
Testing Your CSP
Use the Google CSP Evaluator to analyze your policy for weaknesses. Browser DevTools (Network tab → response headers, and the Console for CSP violations) are indispensable during development.
A strong CSP, combined with HTTPS, HTTP security headers like X-Content-Type-Options, and input validation, forms a robust layered defense for any web application.