Skip to main content
Back to Learning Center

Content Security Policy (CSP): How It Works and Its Limitations

Content Security Policy (CSP) is an HTTP response header that tells the browser which sources of code, images, and connections a page is allowed to load. This guide covers how the header works, its configuration, and where it falls short of full client-side protection.

Oct 20, 2025
Content Security Policy (CSP): How It Works and Its Limitations

A Content Security Policy (CSP) is an HTTP response header that tells the browser which sources of scripts, styles, images, and connections a page is allowed to load. Anything not on that allowlist is blocked before it executes. Standardized by the W3C, CSP is the primary browser-native defense against cross-site scripting (XSS) and script injection.

TL;DR: Content Security Policy

  • CSP declares an allowlist per resource type (script-src, img-src, connect-src, and so on). Anything not on the list is blocked by the browser before it executes.
  • It ships as an HTTP response header or a <meta> tag in the page <head>, and Report-Only mode lets you test a policy without breaking the site.
  • CSP is a baseline, not a complete defense. It controls where a script loads from, not what the script does once it runs, so a compromised but allowlisted domain still executes.
  • CSP alone is not enough for PCI DSS 4.0.1 compliance. Requirement §6.4.3 needs script inventory and authorization; §11.6.1 needs tamper detection. Both go beyond what CSP can enforce, as the Polyfill.io 2024 incident showed.

What does CSP stand for?

CSP stands for Content Security Policy. It is an HTTP response header (also settable via a <meta> tag in the page <head>) that tells the browser which sources of scripts, styles, images, and connections a page is allowed to load, and blocks anything not on that allowlist before it executes.

Understanding Content Security Policy (CSP)

Content Security Policy (CSP) is a browser security feature that was implemented to mitigate certain types of browser-based attacks, like cross-site scripting. The CSP was standardized by the World Wide Web Consortium (W3C) in the CSP Level 3 specification, it allows a website to send a set of rules (via HTTP response headers or <meta> tags inside the HTML <head>) that instructs the browser which content sources are allowed.

These rules, called directives, specify approved origins for scripts, images, styles, iframes, and more. The primary purpose of using CSP is to have full control of where scripts are loaded from, along with controlling which scripts a page is permitted to execute, thus attempting to prevent injected or unauthorized scripts from running.

For example, a CSP directive might state that scripts should only load from the site’s own domain (done by using ‘self’), or from specific trusted domains. The browser will then block any script file or inline script that’s not from an allowed source, providing a crucial defense against XSS attacks where an attacker tries to inject malicious <script> tags or code into a site. These days, most browsers also support CSP ‘Report-only’ mode. This allows developers to test a policy safely. When enabled, policy violations are logged to a reporting endpoint instead of being blocked right away. When CSP is being deployed for the first time, this is a best practice.

How Content Security Policy (CSP) Works

A Content Security Policy is delivered to a browser via the HTTP response header named Content-Security-Policy, or via a meta tag in the HTML <head>. The policy consists of directives separated by semicolons, and each directive controls a specific resource type.

  • ‘script-src’ self: allows scripts only from the same origin. Any <script> from another domain (or inline script code) is blocked by the browser.
  • ‘connect-src’ self https://api.domain.com : allows AJAX/XHR/fetch calls only to the same site, or to the trusted domain api.domain.com. This prevents malicious code from exfiltrating data to unknown servers from the site.
  • img-src ‘self’ data: can be used to only load images from the same site and block external images - which can be used to prevent data leaks via image requests.

CSP Nonces and CSP Hashes

CSP also supports advanced mechanisms such as nonces (‘nonce-abc123’) and hashes (‘sha256-xyz…’). These allow inline scripts to execute safely by cryptographically proving their integrity. Instead of banning all inline code, developers can selectively authorize specific scripts, improving flexibility without sacrificing security.

There are multitudes of other directives that can be used for other data types like media, fonts, iframes, etc. but the core idea of using a Content Security Policy is to whitelist trusted sources. When the browser loads a page and asks what content to load, it will first refer to the CSP and enforce these rules on every load. Any script or resource that loads this policy won’t be loaded. For a detailed implementation guide, check the OWASP CSP Cheat Sheet.

Common CSP Directives And Their Purpose

DirectivePurposeTypical Use Case
default-srcSets a baseline policy for all resources when no other rule applies.Start strict: default-src 'none';
script-srcControls which JavaScript sources are allowed.Whitelist 'self', CDNs, or use nonce/hash-based scripts.
style-srcLimits where CSS can load from.Use 'self'; avoid 'unsafe-inline' when possible.
img-srcDefines trusted image sources.Prevent data leaks via external image calls.
connect-srcRestricts AJAX, fetch, and WebSocket destinations.Block data exfiltration to unknown domains.
frame-ancestorsSpecifies which sites may embed your pages in iframes.Prevent clickjacking: frame-ancestors 'none';
report-uri / report-toDefines where CSP violation reports are sent.Log and analyze CSP breaches for policy tuning.

CSP Header Examples You Can Copy Today

Below are copy-paste starting points for the most common CSP deployments. Test in Content-Security-Policy-Report-Only mode first, watch the reports for legitimate scripts that would be blocked, then move to enforcing mode.

1. Strict starter policy (default deny + allowlist)

Content-Security-Policy:
  default-src 'none';
  script-src 'self' https://cdn.example.com;
  style-src 'self' 'unsafe-inline';
  img-src 'self' data: https:;
  connect-src 'self' https://api.example.com;
  font-src 'self' https://fonts.gstatic.com;
  form-action 'self';
  frame-ancestors 'none';
  base-uri 'self';
  upgrade-insecure-requests;
  report-uri /csp-report;
  report-to csp-endpoint;

2. Nonce-based inline scripts

Content-Security-Policy: script-src 'nonce-r@nd0mNonceHere' 'strict-dynamic';
<script nonce="r@nd0mNonceHere">
  // this script executes; anything without the matching nonce does not
</script>

3. Hash-based inline scripts

Content-Security-Policy: script-src 'sha256-B2yPHKaXnvFWtRChIbabYmUBFZdVfKKXHbWtWidDVF8=';

Generate the hash with: echo -n "your inline script contents" | openssl dgst -sha256 -binary | openssl base64

4. Report-Only mode (no blocking, just monitoring)

Content-Security-Policy-Report-Only:
  default-src 'self';
  script-src 'self' https://cdn.example.com;
  report-to csp-endpoint;

5. Report-To endpoint (modern reporting)

Report-To: {"group":"csp-endpoint","max_age":10886400,"endpoints":[{"url":"https://example.com/csp-report"}]}
Content-Security-Policy: default-src 'self'; report-to csp-endpoint;

6. Sample violation payload

{
  "csp-report": {
    "document-uri": "https://example.com/checkout",
    "referrer": "",
    "violated-directive": "script-src 'self'",
    "effective-directive": "script-src",
    "original-policy": "default-src 'self'; report-uri /csp-report",
    "disposition": "enforce",
    "blocked-uri": "https://malicious.example/skimmer.js",
    "line-number": 42,
    "source-file": "https://example.com/checkout",
    "status-code": 200,
    "script-sample": ""
  }
}

7. Express.js middleware

app.use((req, res, next) => {
  res.setHeader(
    "Content-Security-Policy",
    "default-src 'self'; script-src 'self' https://cdn.example.com; report-uri /csp-report",
  );
  next();
});

app.post("/csp-report", express.json({ type: "application/csp-report" }), (req, res) => {
  console.log("CSP violation", req.body);
  res.sendStatus(204);
});

8. Cloudflare Worker

export default {
  async fetch(request, env) {
    const response = await fetch(request);
    const headers = new Headers(response.headers);
    headers.set(
      "Content-Security-Policy",
      "default-src 'self'; script-src 'self' https://cdn.example.com; report-to csp-endpoint",
    );
    return new Response(response.body, { status: response.status, headers });
  },
};

Expanded directive reference

Beyond the seven directives above, CSP defines several more that show up in production hardening work:

DirectivePurpose
strict-dynamicTrusts scripts loaded by an already-trusted (nonce or hash) script. Simplifies CDN loading.
upgrade-insecure-requestsAuto-upgrades http: subresources to https:. Kills mixed-content warnings.
require-trusted-types-forEnforces Trusted Types on dangerous DOM sinks (e.g. innerHTML, Function()).
sandboxApplies iframe-style sandboxing to the whole page. Powerful, requires careful testing.
form-actionRestricts where <form> submissions may POST to. Blocks phishing redirects.
base-uriLocks the <base> element to specific origins. Prevents <base> injection attacks.
manifest-srcControls where a web-app manifest may load from.
media-srcRestricts <audio> and <video> sources.
object-srcRestricts <object>, <embed>, <applet>. Set to 'none' on modern sites.
worker-srcRestricts Web Worker and Service Worker sources.

Watch out for 'unsafe-inline' and 'unsafe-eval'. Both are still supported but disable most of CSP’s protection against XSS. 'strict-dynamic' with nonces is the modern replacement path.

See it in your own site

CSP is one layer of client-side defense, and a critical one for PCI DSS 4.0.1 compliance. But CSP alone did not stop the Polyfill.io attack in 2024 because the compromised domain was on the allowlist. cside layers script inventory and payload analysis on top of CSP to close the gap. Free tier available. Teams evaluating CDN-native options alongside dedicated solutions can compare cside vs Cloudflare client-side security for a side-by-side of session coverage and PCI DSS evidence depth.

How does CSP prevent browser-based attacks?

Mitigating XSS attacks

In a cross-site scripting (XSS) attack, an attacker finds a way to inject and execute malicious JavaScript in your page, typically through an unsanitized input. By default a CSP blocks inline scripts from executing unless a directive explicitly allows it, so an injected <script>evilCode()</script> never runs, provided 'unsafe-inline' is not in the policy.

Blocking unauthorized third-party scripts

Many sites load third-party scripts for analytics, user tracking, and advertising. A CSP lets site owners limit which external origins may deliver scripts. If you only want to serve content from analytics.example.com and nothing else on example.com, the script-src directive can allow exactly that origin.

Preventing data exfiltration

A CSP can restrict the actions available to a page, which stops malicious JavaScript from sending data back to an attacker-controlled domain. The connect-src directive blocks network requests to unauthorized servers, and pairs with form-action to ensure form data is only ever posted to your own domain.

Enforcing safe browsing practices

A CSP also has directives that improve baseline security:

  • upgrade-insecure-requests forces the browser to load all resources over HTTPS, preventing mixed-content issues.
  • frame-ancestors prevents clickjacking by disallowing your page from being embedded in an attacker-controlled frame.

Combined, these policies result in a strong client-side baseline for modern web apps.

Security Limitations of CSP

Using a Content Security Policy provides strong protection but it isn’t an end-all-be-all solution for your site, as highlighted in our post “Why Content Security Policy Doesn’t Work”.

Policies can drift over time, and allowlists don’t inspect code behavior. Every major browser implements CSP in its own, slightly different way. Chrome, Firefox, Safari, and Edge all support CSP Level 3; reporting behavior and violation formats may vary. To validate and maintain your policy, regularly test it with browser developer tools and automated scanners such as Mozilla Observatory or SecurityHeaders.io. 

Pairing a CSP with an active client-side security layer like cside to add real-time inspection and blocking to third-party scripts on your site gives you a great layer of defense, with peace of mind for your customers. From a governance perspective, documenting CSP updates and monitoring violation reports improves auditability and long-term compliance with frameworks such as ISO 27001 and OWASP ASVS.

Does CSP Work for PCI DSS 6.4.3 Compliance?

Under PCI DSS 4.0.1 Requirement 6.4.3, merchants must prove every client-side script is authorized and prove script integrity. CSP and SRI get you part of the way there. CSP limits which domains can load scripts, and SRI checks that a file’s code hasn’t changed. But together they’re a static solution to a dynamic problem. Dynamic scripts update and hashes break. Manual maintenance of CSP lists is nearly impossible.

Most modern sites use dynamic third-party scripts so these controls degrade fast. This approach will typically fall short of the required evidence needed for PCI 6.4.3.

A Content Security Policy (CSP) Example: When a Strict CSP Broke Production

The day check-out stopped working: How a Strict CSP Broke Production

It all began with just an ordinary new deployment. Nothing special, just a new Content Security Policy to make an ecommerce shop more secure and block attackers from sneaking in bad code.

The clean default-src ‘none’ was set without testing. And so, the moment the CSP was activated, the website blocked services it actually needed. Analytics stopped working and worst of all, the payment system was blocked. Customers couldn’t complete their orders and the check-out system broke. Developers went to work and switched the header to Content-Security-Policy-Report-Only and collected violation logs. From there, they built an allowlist (script-src ‘self’ https://pay.examplecase.com) with all the services the webshop needed to run properly.

Refining the CSP enabled deployment with zero breakage. Unfortunately, this oversight is a common mishap when teams manage CSP internally. Forgetting to whitelist a new marketing script, technical misconfigurations, or issues with dynamic scripts that break hashing protocols make CSP a nightmare to manage at scale.

Generate and Maintain Your CSP Automatically with cside

Hand-writing and maintaining a Content Security Policy is where most teams get stuck: every new marketing tag, dynamic third-party script, or vendor endpoint change can break the policy or silently widen the allowlist. cside removes that manual work.

cside watches the scripts your site actually loads and generates a ready-to-deploy CSP header for you from real browser sessions, then keeps it current as those scripts change, so you are not chasing allowlist updates by hand. You get a Content Security Policy that reflects what your site really runs, plus the script inventory and tamper detection that CSP alone can’t provide for PCI DSS 4.0.1 §6.4.3 and §11.6.1.

There is nothing to build. Sign up for the free tier, add your domain, and cside starts generating your CSP from live traffic. Explore the CSP management solution, or see how cside closes the gaps a CSP leaves open.

Simon Wijckmans
Founder & CEO

Founder and CEO of cside. Previously a product manager on Cloudflare Page Shield (now Cloudflare Client-Side Security). Co-chair of the W3C Anti-Fraud Community Group and a Forbes 30 Under 30 honoree. Building accessible security against client-side attacks, web security is not an enterprise-only problem.

Monitor and Secure Your Third-Party Scripts

Gain full visibility and control over every script delivered to your users to improve site security and performance.

Start free, or try Business with a 14-day trial.

cside dashboard interface showing script monitoring and security analytics

FAQ

Frequently Asked Questions

Input validation helps reduce risk of XSS injection but doesn't necessarily stop it. A CSP provides an additional layer of defense by restricting what resources can and can't be loaded based on its source. Meaning that even if a piece of malicious code slips through, the browser can still stop it from running.

Unfortunately not. CSP can reduce the attack surface for cross-site scripting, but it's not the silver bullet. Misconfigurations of the CSP, overly broad allowlists, or the use of `unsafe-inline` can still leave your site vulnerable. Many websites that use CSP still allowlist googletagmanager.com and anyone can use that domain to host code.

If not implemented properly it would. A CSP can block legitimate resources such as third-party libraries your website relies on. That's also why it's important to test and to adjust your rules before enforcing them.

Maintenance can be challenging, especially if your site relies heavily on dynamic third-party tools. Policies need regular updates as tools update. Your marketing tools will most likely not give you a heads up if they start sending data to a new endpoint. A service like cside can help ease some of the ongoing maintenance.

Generally, no but there are caveats. The browser will simply check the resources against the CSP before blocking them. The performance implications are negligible compared to the security benefits you would gain. The concern is mostly configuration time. When using CSP to its limits, meaning using the full CSP header length, it does increase the packetsize considerabily which can have performance implications at scale or to users on low bandwidth connections.

Book a demo

Want to walk through this with an engineer?

Thirty minutes, on your own site. Not a slide deck.

We'll show you:

Which third-party scripts are running on your site right now
Where you stand on PCI DSS 6.4.3 and 11.6.1
How much of your traffic is bots and AI agents

Rather just send a question?

Finding open slots…

Real humans only. We'd know.

Having trouble booking? Open scheduler in a new tab

What are you trying to solve?

Tell us in a line and we'll come back with something useful, not a generic pitch.

We usually help with:

Seeing which third-party scripts run on your site
PCI DSS 6.4.3 and 11.6.1 evidence
Bots, AI agents and account takeover

Prefer to just book a time? Pick a slot instead