Content Security Policy 2026
Content Security Policy (CSP) is a critical HTTP response header designed to prevent a broad class of code injection vulnerabilities by specifying which dynamic resources are allowed to load on a web page. With today's complex web applications relying heavily on external scripts and third-party tools, CSP provides a defense layer that directly limits the vectors attackers can exploit.
At its core, CSP gives developers fine-grained control over what content the browser can execute. This includes restricting JavaScript, stylesheets, images, fonts, and even inline code, based on origin or exact content hash. By defining these rules, CSP creates a security perimeter that prevents unauthorized code execution.
Cross-site scripting (XSS) remains one of the most common and dangerous attack vectors targeting web platforms. CSP minimizes XSS risk by allowing only trusted scripts and blocking all inline scripts unless explicitly allowed. It also neutralizes content injection threats that might originate from improperly sanitized user input or third-party integrations.
As threat actors adopt more sophisticated techniques for compromising browser behavior, CSP offers a powerful and standardized way to contain execution privileges to approved domains and assets. In modern browser environments, properly configured CSP headers significantly reduce vulnerability surface areas, making them essential for secure web development practices.
Web applications face a wide array of security vulnerabilities—many of which stem from improper input validation, insecure configurations, or outdated components. Three classes of threats dominate the landscape:
Each of these vulnerabilities emerges from the same underlying issue: a failure to restrict user-controlled content from impacting application behavior or data flow. Improper access control, exposed APIs, and insecure session management further expand the attack surface.
XSS presents one of the most persistent and damaging types of client-side threats. Through injection of malicious JavaScript into webpages viewed by other users, attackers can harvest credentials, hijack sessions, or inject misleading interface elements (UI redressing).
Three major variants of XSS exist:
The most exploited weakness? Developers trusting user input. Without server-side filtering and client-side restrictions, even a small input field becomes a powerful attack vector.
A well-defined Content Security Policy (CSP) significantly limits what resources the browser can load and execute. This restricts execution of unauthorized JavaScript, blocking most forms of XSS even before the malicious code has a chance to run.
By declaring trusted content sources using headers or meta tags, CSP transforms the browser into an enforcement point for your security boundary. In effect, it reduces the range of locations from which scripts, styles, images, and other resources may be fetched or executed.
When used with mechanisms like nonces or script hashes, CSP can disable inline scripts altogether—closing off entire classes of injection pathways. Instead of relying solely on input validation or output encoding, CSP establishes defensive layers both in the browser and in policy logic.
A Content Security Policy (CSP) is a declarative layer of defense that defines which content sources are permitted to load and execute on a given web page. Configuration happens in one of two ways—via the HTTP Content-Security-Policy response header or through a <meta http-equiv="Content-Security-Policy"> tag in the HTML document’s <head> section.
Server-delivered headers take precedence. Most production environments rely on them due to their reliability and scope. Meta tags offer an alternative for static files without backend control, but browsers will ignore a <meta> tag if a CSP header has already been received.
Each CSP is composed of one or more directives—these are rules telling the browser what kind of content to allow and from where. Every directive contains zero or more source expressions, which act as filters for content origins. When no specific directive is provided for a content type, the browser falls back to the default-src directive.
script-src, img-src, style-src).'self', https://cdn.example.com, or data: that specify allowed origins.default-src when a more specific directive is not set.Here are sample CSP policy strings that illustrate typical configurations:
default-src 'self';. Allows all content only from the originating domain.default-src 'self'; script-src 'self' https://cdn.example.com;. Scripts can load from the origin or a specified CDN.script-src 'self';. Disallows inline JavaScript and usage of eval() unless explicitly allowed.Combine multiple directives to create nuanced policies that reflect real-world complexity. Structure and specificity directly impact enforcement—browsers parse directives from left to right, applying them in the order written.
When a browser receives a CSP header or reads a meta tag, it parses the string into its constituent directives and constructs an internal policy object. As the page loads, each request is evaluated against the applicable directive—if the requested resource matches an allowed source, it proceeds; if not, it’s blocked and optionally logged.
Consider a page loading a remote script. With a policy like script-src 'self';, the browser blocks the request to https://external.domain.com/script.js, even if the file exists and is otherwise functional. The result is deterministic: a match allows execution, a mismatch results in silent blocking or an entry in the developer console—if a report-uri or report-to endpoint is configured, violation details are sent there.
This direct mechanism enforces predictable behavior with zero ambiguity, making CSP an effective control against content injection and resource tampering.
Cross-Site Scripting (XSS) attacks exploit the browser’s trust in the displayed content, allowing attackers to execute arbitrary JavaScript in the context of a user’s session. Content Security Policy halts this behavior at the browser level by restricting how and where scripts load and execute.
The directive script-src defines trusted sources for JavaScript. When inline scripts are disallowed—by omitting 'unsafe-inline' from script-src—the browser ignores any script tags embedded directly in the HTML. As a result, attackers can't inject and run JavaScript simply by compromising the HTML structure.
Object embedding via the object, embed, or applet tags opens similar avenues for JS-based exploitation. The object-src directive limits which origins such resources can be loaded from, effectively cutting off this attack vector.
<script>document.cookie</script>. Without CSP, this script executes in every visitor’s browser, exfiltrating session data.Content-Security-Policy: script-src 'self'; object-src 'none';. Now, when the malicious comment loads, the inline script is blocked at execution. The browser disregards the tag, and the attack fails.This server-driven policy shifts control away from the client and sharply reduces the effectiveness of XSS injection. A single CSP header eliminates an entire class of script-based payloads.
script-src governs script loading behavior. It can whitelist specific domains, enforce integrity checks, and reject inline code execution. Without 'unsafe-inline' or 'unsafe-eval', inline scripts and eval()-like functions produce violations and get blocked instantly.
object-src adds a second layer of restraint. Attackers sometimes rely on Flash, Silverlight, or malicious Java applets to deliver scripted payloads. By setting object-src 'none', the browser refuses to load embedded objects altogether, cutting off these legacy threats at their root.
Directives in concert produce a hardened runtime environment. They don't just react to known exploits—they make entire exploit categories structurally impossible to execute in modern browsers.
Content Security Policy gains its power through an extensive set of directives. These directives control which content the browser can load and execute, effectively reducing the surface area for attacks like XSS, clickjacking, or data injection. Understanding both core and advanced directives enables precise control over your web application's resource behavior, communication flow, and execution environment.
Core CSP directives define trusted sources for each type of resource—the building blocks for any robust CSP configuration. They address the primary vectors for malicious payloads:
@font-face rules.Advanced directives extend CSP's capabilities, offering additional safeguards beyond basic resource allowances. These are particularly effective for mitigating UI redressing and more complex injection attacks.
frame-src and worker-src.<base> tags.default-src as a Fallbackdefault-src acts as a global fallback for any directive not explicitly mentioned. For example, if script-src isn’t defined, the browser will inherit rules from default-src. This encourages concise yet effective policies but mandates awareness—omitting specific directives unintentionally defers control.
'self', 'none', and data:These source expressions appear across multiple directives, enabling flexible composition while maintaining strict control. Inject only what’s needed—no more, no less.
Any script embedded directly in HTML—whether within <script> tags or as event handlers—opens a direct line for attackers to inject malicious code. These inline scripts don’t require external file access, making them attractive entry points for cross-site scripting (XSS) attacks. Once an attacker slips malicious JavaScript inside your page, it can exfiltrate cookies, manipulate DOM elements, or even hijack entire user sessions.
CSP mitigates this risk by blocking all inline scripts unless explicitly allowed. The default behavior of CSP with 'script-src' disallows executing inline JavaScript, forcing developers to change how scripts are delivered.
Nonces (short for "numbers used once") are cryptographically strong, randomly generated strings attached to each script tag that should be permitted to run. When a page loads, the server generates a new nonce for that request and includes it in the Content-Security-Policy header:
Content-Security-Policy: script-src 'nonce-abc123xyz';
In the HTML, each inline script must contain a matching nonce attribute:
<script nonce="abc123xyz">
// This script is allowed to run
</script>
If the nonce in the header and the script tag align, the browser executes the script. Otherwise, it gets blocked. Since nonces are regenerated for each request, previously captured ones become useless in subsequent attempts. This dynamic nature makes nonces effective against reflected and stored XSS.
Instead of using nonces, site administrators can approve specific script blocks by calculating their SHA-256, SHA-384, or SHA-512 hash values and including them in the CSP:
Content-Security-Policy: script-src 'sha256-3zLcjfCZo6vIvY3NqsiYptwnRxJMFbnhNTWZ0gf7xWk=';
This method ties script execution directly to its content. Any change in the script content will produce a different hash, causing the browser to reject the script. Hash-based CSP is particularly useful for static sites or known, unchanging scripts where regenerating nonces would be unnecessary overhead.
Choosing between nonces and hashes depends on the context: use hashes for static, rarely-changing scripts and nonces for dynamic environments requiring flexible, per-request control. Combine both in a policy to achieve wide coverage while retaining control absent of unsafe-inline fallbacks.
Launched in 2012, Content Security Policy Level 1 introduced a fundamental mechanism for reducing the attack surface of web applications. It allowed developers to define which sources could load specific types of content, such as scripts, styles, images, fonts, and iframes. By default, resources not explicitly permitted by the default-src directive were blocked. This level emphasized control over external resource loading, which significantly reduced exposure to Cross-Site Scripting (XSS) and data injection attacks.
However, Level 1 did not restrict inline scripts or event handlers, making it insufficient against certain classes of DOM-based XSS vulnerabilities. For development teams relying heavily on inline <script> and onclick attributes, Level 1 functioned more as a deterrent than a complete line of defense.
Released by the W3C in 2016, CSP Level 2 introduced mechanisms to block inline scripts and prevent execution of code bound to HTML attributes. It enabled stricter enforcement through two key directives: script-src (with 'unsafe-inline' disabled) and nonce- or hash-based script whitelisting.
onclick, onload, and onerror could now be blocked entirely.'unsafe-inline' by using a per-request nonce or a SHA-based hash to whitelist approved inline code.With these additions, CSP became a viable solution for enterprises aiming for defense-in-depth without restructuring their client-side JavaScript entirely.
CSP Level 3 focused on improving the developer experience and providing stronger enforcement mechanisms. Adopted as a W3C Candidate Recommendation in 2016 and refined over time, this level introduced:
script-src URLs and allows dynamic scripts to run only if they originate from a script with a valid nonce or hash.When using strict-dynamic, developers no longer need to maintain a static list of valid script sources. As long as the initial script has a valid nonce or hash, it can dynamically load other scripts—offering both flexibility and safety.
Not every application can jump directly to CSP Level 3. Migrating involves progressive enhancement and iterative testing. The following phased approach enables secure adoption:
default-src and audit external resources. Use Content-Security-Policy-Report-Only to observe violations.'unsafe-inline' and begin using nonces or hashes for inline scripts. Refactor your JavaScript to eliminate inline event handlers and eval() usage.strict-dynamic, minimize static whitelists, and incorporate Trusted Types where supported. Use violation reports to fine-tune policy rules and uncover hidden dependencies.Consistency across environments matters. Moving to higher levels of CSP protection requires collaboration between front-end and back-end teams, as well as CI/CD pipeline integration to automate policy validation.
Content Security Policy (CSP) has seen broad adoption across modern browsers, but compatibility varies depending on the CSP level and specific directives used. All major browsers—Chrome, Firefox, Safari, Microsoft Edge, and Opera—support CSP Level 1. This baseline includes directives such as default-src, script-src, and style-src, which cover fundamental protections against script injection and resource loading threats.
CSP Level 2 introduced nonces and hashes for inline scripts, significantly strengthening the policy against Cross-Site Scripting (XSS). Chrome (version 40 and above), Firefox (version 35+), Edge (15+), and Safari (10+) fully support Level 2. Internet Explorer never adopted Level 2, maintaining partial implementation of Level 1 through an older X-Content-Security-Policy header that's no longer actively developed.
CSP Level 3 expands on these protections with more granular directives like strict-dynamic, report-to, and worker-src. However, support here is less uniform. Chrome and Firefox have implemented many Level 3 features; Safari and Edge are still catching up. Opera, leveraging Chromium’s engine, aligns closely with Chrome’s capabilities.
When enforcing CSP across diverse user environments, graceful degradation ensures that older browsers still render content, even without full policy enforcement. Browsers lacking CSP support simply ignore the header. This fallback behavior allows sites to function while newer browsers enforce the defined security policies rigorously.
To support legacy environments:
Developers can use a variety of tools to test CSP coverage and browser compatibility. These tools accelerate debugging and reveal inconsistencies between browsers.
Regular testing with these tools across different browsers and devices helps enforce robust security while avoiding regressions in older environments.
report-uri and report-to DirectivesEvery time a browser blocks a resource based on a site's Content Security Policy, it can generate a violation report. These reports provide structured, machine-readable data that details what was blocked and why. Two directives handle this reporting mechanism: report-uri and report-to.
report-uri is the legacy directive — still supported by many browsers — that sends violation reports to a specified URI. For example:
Content-Security-Policy: default-src 'self'; report-uri https://example.com/csp-report
report-to, introduced in CSP Level 3, allows for broader and more structured reporting across features like CSP, Network Error Logging, and Feature Policy. It connects to a named Reporting-Endpoints header, offering a more powerful and flexible approach:
Content-Security-Policy: default-src 'self'; report-to csp-endpoint Reporting-Endpoints: csp-endpoint="https://example.com/reports"
Violation reports are not just technical logs — they’re high-signal indicators of real-time attacks or misconfigurations. Catching a report about an unexpected inline script or an unauthorized third-party content load can point directly to a vulnerability or a compromised dependency.
Parsing, aggregating, and analyzing CSP violation reports adds a dynamic layer to security observability. Enterprises route these reports into SIEMs like Splunk, ELK Stack, or via serverless intake APIs that trigger alerts in systems like PagerDuty or Slack.
To avoid noise, aggregate and prioritize based on violation frequency, blocked URI patterns, and affected user agents. Format the reports in JSON for easier indexing. Here’s what a sample report looks like:
{
"csp-report": {
"document-uri": "https://example.com/index.html",
"referrer": "",
"violated-directive": "script-src",
"effective-directive": "script-src",
"original-policy": "default-src 'self'; script-src 'nonce-abc123'; report-uri /csp-report",
"blocked-uri": "http://malicious.com/inject.js",
"line-number": 42,
"source-file": "https://example.com/app.js",
"status-code": 200
}
}
Content-Security-Policy-Report-OnlyBefore enforcing a new CSP policy, it helps to run it in Report-Only mode. The Content-Security-Policy-Report-Only header allows applications to monitor what would be blocked — without blocking anything in reality.
This mode exposes gaps, detects overlooked inline scripts, and flags third-party content misalignments without breaking production flows. It’s an essential phase when migrating away from unsafe-inline or enabling hashed script loading.
Content-Security-Policy-Report-Only: script-src 'self' 'nonce-random123'; report-uri https://report.example.com/csp
Use report-only mode for at least one full user session cycle, ideally over multiple browsers. This generates enough violation data to inform policy finalization before switching to enforcement mode.
Rolling out CSP in enforcement mode without prior testing results in immediate disruptions. Launching with a Content-Security-Policy-Report-Only header allows full visibility into how existing site resources and scripts behave under CSP restrictions — without actively blocking content. During this phase, monitor CSP violation reports to flag any unexpected loading paths or third-party scripts currently in use.
Before defining restrictive CSP rules, map your site’s full inventory of externally loaded assets. This includes JavaScript files, CSS, fonts, images, and iframes. Identify inline scripts and determine whether they rely on dynamic evaluation methods like eval(). For large websites, automated scanning tools such as Google’s CSP Evaluator or Mozilla Observatory streamline this discovery process.
Using wildcards in host source declarations — such as *, *.example.com, or https: — opens the door to unintended content injection. A robust policy names exact, verified domains or subdomains per directive. For example:
script-src https:*script-src https://cdn.example.comOnly include domains where you control content or have thoroughly vetted the provider. That includes CDNs, analytics services, widget vendors, and payment gateways.
Once the report-only header exposes all third-party interactions and inline behavior, refine your policy step-by-step. Transition to enforcement mode, beginning with conservative domains under default-src, then layer directives like script-src, style-src, and connect-src with granular precision.
Introduce nonces or hashes as part of this tightening process where static inline scripts must be maintained. Periodically test under simulated attack conditions to ensure the policy genuinely deflects code injection routes.
Web environments evolve constantly. New features, third-party updates, or frontend frameworks introduce additional content loaders. Schedule reviews of your CSP at regular intervals, ideally aligned with sprint cycles or release milestones. Log violations sent via the report-uri or report-to endpoints and audit any newly added domains or inline code patterns that bypass existing rules.
Not all content requires the same level of control. Apply directives that target specific categories—such as img-src for tracking pixels or frame-ancestors to prevent clickjacking. Avoid reusing default-src broadly if other directives offer tighter scoping opportunities.
unsafe-inline absent or being phased out?Treat CSP as a living layer of your site’s defensive architecture — adaptive, iterative, and increasingly stringent with every deployment cycle.
Deploying a Content Security Policy (CSP) transforms how browsers handle potentially harmful resources. It directly reduces the attack surface by limiting where content can be loaded from, allowing site owners to define their own rules of engagement. CSP does more than mitigate cross-site scripting (XSS); it provides structural resilience against a wide range of injection-based threats.
Pair CSP with other response headers to build a multi-layered defense strategy. Each header serves a different purpose:
Applied collectively, these headers form a defensive perimeter that hardens web content against the most common exploits. CSP anchors this group by dictating what the browser must block or permit when rendering the page.
Threats evolve daily. CSP should evolve with them. Regularly review your policies and use reporting endpoints to identify policy violations before they translate into real vulnerabilities. Historical data from Content-Security-Policy-Report-Only headers reveals gaps in coverage and opportunities to tighten rules without breaking functionality.
Automated tools can assist, but human oversight remains the pivot point. Incorporate policy review as part of your release checklist. Assign responsibility to a role in your security or DevOps team. Set thresholds for violations—when exceeded, trigger audits.
To reduce unauthorized script execution effectively:
'unsafe-inline' and 'unsafe-eval' completely.Every browser-executed script fills a role. When unregulated, it also opens a door. Locking that door—precisely and deliberately—keeps malicious code out without impacting legitimate functionality.
Making CSP integral to your security posture doesn’t just restrict misbehavior—it builds trust, both from users and browsers. Every directive enforced, every misunderstood script denied, strengthens the integrity of your site’s front line.
