Content Security Policy (CSP) is the most powerful HTTP security header available to web developers. It tells the browser exactly which sources of content are permitted, effectively neutralizing entire classes of injection attacks including cross-site scripting (XSS), data exfiltration, and malicious resource injection.
This article walks through CSP from first principles to advanced techniques like nonce-based policies and report-only deployment. CSP is a key component of the headers covered in our complete security headers guide.
CSP is delivered via the Content-Security-Policy HTTP response header (or a <meta> tag, though the header is preferred). It defines a set of directives that control which resources the browser may load for a given page.
When a resource violates the policy — for example, an inline script when script-src does not include 'unsafe-inline' — the browser blocks it and optionally sends a violation report to a URL you specify.
Each directive controls a specific resource type:
default-src — fallback for any directive not explicitly set.script-src — JavaScript sources. The most security-critical directive.style-src — CSS sources.img-src — image sources.font-src — web font sources.connect-src — URLs that can be contacted via XHR, fetch, WebSocket.frame-src — sources that can be embedded in iframes.frame-ancestors — which origins can embed this page (replaces X-Frame-Options).base-uri — restricts the URLs that can appear in <base> elements.form-action — restricts form submission targets.object-src — plugin sources (Flash, Java). Set to 'none'.media-src — audio and video sources.worker-src — Web Worker and Service Worker sources.Start with the most restrictive policy possible and relax it only where necessary:
Content-Security-Policy: default-src 'none'; script-src 'self'; style-src 'self'; img-src 'self'; font-src 'self'; connect-src 'self'; frame-ancestors 'none'; base-uri 'self'; form-action 'self'This policy allows resources only from your own origin and blocks everything else. It is a strong starting point for applications that do not load third-party resources.
'self' — the page's own origin (scheme + host + port).'none' — block everything.'unsafe-inline' — allow inline scripts/styles. Avoid this.'unsafe-eval' — allow eval() and similar. Avoid this.'nonce-{random}' — allow specific inline elements with a matching nonce attribute.'strict-dynamic' — trust scripts loaded by already-trusted scripts. Powerful for complex apps.https://cdn.example.com, https:, data:Nonces are the recommended way to allow inline scripts without using 'unsafe-inline'. Generate a cryptographically random nonce per request, include it in the header and in each script tag:
# Header
Content-Security-Policy: script-src 'nonce-4AEemGb0xJptoIGFP3Nd'
# HTML
<script nonce="4AEemGb0xJptoIGFP3Nd">
// This script will execute
</script>
<script>
// This injected script will be blocked
</script>The nonce must be different on every response. Never use a static nonce — it would defeat the purpose entirely.
Before enforcing a new CSP, deploy it in report-only mode to see what would break without actually breaking it:
Content-Security-Policy-Report-Only: default-src 'self'; report-uri /csp-reports; report-to csp-endpointViolation reports are sent as JSON POST requests to the specified URI. Monitor these for a week or two, fix any legitimate violations, and then switch to enforcement.
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:; font-src 'self'; frame-ancestors 'none'; base-uri 'self'; form-action 'self'" always;Header always set Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:; font-src 'self'; frame-ancestors 'none'; base-uri 'self'; form-action 'self'"'unsafe-inline' with script-src. This single directive negates most of CSP's XSS protection. Use nonces or hashes instead.https://cdn.jsdelivr.net, an attacker can host malicious scripts there. Use specific paths or 'strict-dynamic'.object-src. If not explicitly set, it falls back to default-src. Always set object-src 'none' unless you need plugins.Use our CSP testing tool to analyze your policy for common weaknesses. You can also audit all your security headers at once. For more on the full suite of security headers, see our complete security headers guide.