How to Add a Content-Security-Policy Header — Beginner’s Guide
Content-Security-Policy (CSP) is one of the most powerful browser security headers — and one of the most commonly missing. This guide explains exactly what it does and how to safely add it to your site, even if you’ve never set one before.
What is a Content-Security-Policy?
CSP is an HTTP response header that tells the browser exactly which sources of scripts, styles, images, fonts, and other resources are allowed to load on your page. Any resource not matching your policy is blocked before it executes.
Without a CSP, if an attacker manages to inject a <script> tag into your page (via a stored XSS vulnerability, a compromised third-party script, or a browser extension injecting content), it runs with the same trust as your own code — accessing cookies, session tokens, user keystrokes, and any data on the page.
CSP is your last line of defense against Cross-Site Scripting (XSS), one of OWASP’s Top 10 vulnerabilities and a common way attackers steal session tokens and user data.
Understanding CSP Directives
A CSP header value is a semicolon-separated list of directives. Each directive controls a different resource type:
| Directive | Controls |
|---|---|
| default-src | Fallback for all resource types not explicitly listed |
| script-src | JavaScript files and inline <script> blocks |
| style-src | CSS files and inline <style> blocks |
| img-src | Images (including data: URIs) |
| font-src | Web fonts (woff, woff2, ttf, etc.) |
| connect-src | fetch(), XMLHttpRequest, WebSocket connections |
| frame-ancestors | Which pages may embed this page in an iframe (replaces X-Frame-Options) |
| base-uri | Allowed values for the <base> element |
A Safe Starter CSP Policy
The policy below is a solid starting point for most websites that load resources only from their own domain plus Google Fonts and a CDN like jsDelivr:
Content-Security-Policy: default-src 'self'; script-src 'self' 'nonce-RANDOM_NONCE'; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; img-src 'self' data: https:; connect-src 'self'; frame-ancestors 'none'; base-uri 'self'; form-action 'self';
Replace RANDOM_NONCE with a per-request random value if you use inline scripts; alternatively use 'sha256-…' hashes. If your site currently uses a lot of inline scripts, you can temporarily add 'unsafe-inline' to script-src to avoid breakage while you audit.
Test before enforcing: Start with Content-Security-Policy-Report-Only instead of Content-Security-Policy. This logs violations to DevTools (or a report-uri endpoint) without blocking anything, letting you refine your policy before locking it down.
How to Add CSP on Nginx
Add the header in your HTTPS server block:
server {
listen 443 ssl;
server_name example.com;
add_header Content-Security-Policy
"default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; img-src 'self' data: https:; connect-src 'self'; frame-ancestors 'none';"
always;
}Long policies should be kept on one line in Nginx config. After editing, test your config with sudo nginx -t before reloading.
How to Add CSP on Apache
Enable mod_headers if needed (sudo a2enmod headers), then add to your VirtualHost:
<VirtualHost *:443>
ServerName example.com
Header always set Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; img-src 'self' data: https:; connect-src 'self'; frame-ancestors 'none';"
</VirtualHost>How to Add CSP on Cloudflare
Cloudflare can inject CSP headers via a Transform Rule:
- Go to your domain in the Cloudflare dashboard → Rules → Transform Rules → Modify Response Header.
- Click Create Rule. Set the Expression to
true(applies to all requests). - Under Then, choose Add → Header name:
content-security-policy, Value: your full policy string. - Click Deploy.
Alternatively, use a Cloudflare Worker to inject the header dynamically (useful if you need per-request nonces for inline scripts).
How to Verify Your CSP
Check the header is present with curl:
curl -sI https://example.com | grep -i content-security
Open Chrome DevTools → Console. Any blocked resource shows a CSP violation error with the exact directive that blocked it, making it easy to refine your policy:
Refused to load the script 'https://cdn.example.com/analytics.js' because it violates the following Content Security Policy directive: "script-src 'self'". Consider adding 'unsafe-inline' or a nonce...
Use the CSP Evaluator tool from Google (csp-evaluator.withgoogle.com) to score the strength of your policy and identify weaknesses.
Not sure if your site has a CSP header?
Run a free Vigilai scan to check CSP, HSTS, X-Frame-Options, TLS, and 4 other headers in seconds — no account needed.