Content Security Policy
CSP (Content Security Policy) is a security mechanism in which the server uses HTTP headers to specify the origins and types of resources a web page may load, causing the browser to block scripts, styles, images, and other content that fall outside that scope. It is one of the most powerful defenses against XSS (Cross-Site Scripting) attacks.
βΆArchitecture Diagram
π RelationshipDashed line animations indicate the flow direction of data or requests
Accepting user input and displaying it on a page is a function found everywhere in web applications -- comments, profiles, search terms, post titles. If a <script> tag is mixed into that input, the browser cannot distinguish whether it is code the developer intended or code an attacker injected. This is an XSS (Cross-Site Scripting) attack. Escaping or filtering input on the server side is the first line of defense, but if even one of hundreds of input paths is missed, the defense is broken. Verifying every path without exception each time a new feature is added is not realistic. CSP flips the approach to this problem. Instead of perfectly filtering input, it tells the browser: 'The only scripts that may execute on this page are those from these sources.' Even if a script is injected past the server-side filter, the browser will not execute code that is not covered by the CSP policy.
XSS has been consistently reported as one of the most common web vulnerabilities since the early 2000s. Defensive techniques such as server-side filtering, output encoding, and automatic framework-level escaping have matured, but as web applications have grown more complex, blocking every input path without exception has become increasingly difficult. The proliferation of third-party scripts, ad networks, analytics tools, and user-generated content has also diversified the origins of code executing within a page. Out of this environment came the idea of 'ensuring only permitted code runs', and in 2012 the W3C published CSP 1.0 as a recommendation. The structure -- where the server embeds policy in response headers and the browser enforces those rules -- makes it possible to limit the blast radius even when developers cannot anticipate every attack. The addition of nonce and hash support in CSP 2.0 made it possible to handle realistic situations where inline scripts are unavoidable.
CSP begins with the server embedding policy in the Content-Security-Policy HTTP response header. The browser parses this policy and checks compliance every time the page loads a resource or executes a script. A policy is composed of directives and source lists. Writing script-src 'self' https://cdn.example.com means that scripts may only be loaded from the same origin ('self') and from cdn.example.com. Scripts from any origin not on that list are refused outright by the browser. Different policies can be set per resource type using style-src, img-src, connect-src, and others; default-src serves as the fallback policy for any type not explicitly specified. Inline scripts are blocked by default. This is the core of CSP's defensive power, since most code injected through XSS is inline. However, when inline scripts are unavoidable due to legacy code or analytics tools, the server can generate a unique nonce value for each response and attach the same nonce to the script tag, permitting only that specific script. Any script injected by an attacker will lack the correct nonce and will still be blocked. Using the Content-Security-Policy-Report-Only header in Report-Only mode reports violations to a designated endpoint without blocking anything. This is extremely useful when first introducing CSP to an existing site, as it reveals which resources would be blocked before the policy is finalized.
Send the policy in the response header
Content-Security-Policy: default-src 'self'; script-src 'self' 'nonce-abc123' https://cdn.example.comRead default-src and script-src together to see what the page allows by default and which script sources are added on top of that baseline.
Only scripts with the matching nonce execute
<script nonce="abc123">
initAnalytics();
</script>The script runs only when its nonce matches the policy. Inline code without that value is blocked by the browser.
CSP and CORS are both security policies enforced by the browser, but they control different things. CSP determines 'which origins' resources may be loaded and executed within this page' -- the server is declaring what is permitted within its own page. CORS deals with 'whether a server permits a JavaScript-initiated request from a different origin' -- the server receiving the request decides whether to allow it. Another distinction worth drawing is the relationship between CSP and server-side input validation. It may seem that having CSP makes input validation unnecessary, but CSP is the last line of defense that blocks execution -- it does not sanitize the input itself. Server-side filtering is the first line of defense, and CSP is the second line that catches code that gets past that filter. Both are needed for a robust defense.
The most practical approach when applying CSP to an existing service for the first time is to start with Report-Only mode. Setting the Content-Security-Policy-Report-Only header with a report-uri directive collects violations at the designated endpoint without blocking anything. Reviewing that data to understand which inline scripts and which third-party origins are actually in use before finalizing the policy prevents the site from breaking unexpectedly. Completely prohibiting inline scripts is ideal, but third-party code like Google Analytics or marketing tags often requires inline scripts. Using unsafe-inline in that case significantly weakens CSP's defenses, so using nonces is preferable. The server generates a unique nonce for each response and adds it only to the script tags that should be permitted. Monitoring CSP violation reports also has the side effect of detecting XSS attempts. A sudden spike in violation reports during normal operation may indicate an attack attempt or an unintended change in a third-party script.