Why does a misconfigured CSP provide false security?

csp-configuration-issuesglobal

A Content Security Policy can exist in the response without restricting anything. Policies composed entirely of reporting directives, fetch directives with overly broad values, or deprecated header names create the appearance of security controls without providing them. Multiple CSP headers interact through intersection semantics that tighten rather than merge policy, producing unexpected blocking. HTTP sources in policies served over HTTPS reintroduce the attack surface that encryption was meant to eliminate. Each of these configuration errors is worse than having no CSP at all, because the presence of the header signals resolved intent.

What’s wrong

The site sends a Content-Security-Policy header that does not meaningfully restrict browser behavior. Four distinct patterns produce this condition. First, a policy containing only non-allowlist directives like report-uri or plugin-types without script-src, default-src, or other fetch directives restricts nothing. Second, multiple CSP headers in the same response use intersection semantics, where the browser applies the most restrictive interpretation across all policies, often blocking resources neither policy intended to block individually. Third, deprecated header names like X-WebKit-CSP or X-Content-Security-Policy are ignored by modern browsers entirely. Fourth, HTTP sources listed in a policy served over HTTPS allow a network attacker to inject resources through the unencrypted channel. In each case, the header exists but the protection does not.

Why it matters

A misconfigured CSP is worse than a missing CSP. When the header is absent, security scanners flag it, penetration testers report it, and teams know the gap exists. When a CSP header is present, these signals disappear. The policy passes automated checks. Compliance checklists mark it complete. The team moves on, believing the control is active. Meanwhile, the browser enforces nothing meaningful. False confidence compounds over time. Dependencies grow unchecked. Attack surface expands without visibility. The header that was supposed to define boundaries instead conceals their absence.

Meaningless CSP: policies that restrict nothing

A Content Security Policy that contains only report-uri, report-to, plugin-types, or sandbox without any fetch directives does not restrict resource loading. The browser processes the header, finds no allowlist directives, and falls back to default behavior: allow everything. This also applies to policies that include fetch directives but set them to values that permit all sources, such as default-src * ‘unsafe-inline’ ‘unsafe-eval’ data: blob:. The policy is syntactically valid. Parsers accept it. Headers are present. But the browser’s actual behavior is identical to having no CSP at all. The only difference is that the header creates an audit trail showing a policy was deployed.

Multiple CSP headers: intersection semantics

When a response contains multiple Content-Security-Policy headers, the browser does not merge them. It applies intersection semantics: a resource must be permitted by every policy to load. This is the most counterintuitive aspect of CSP deployment. If one header allows scripts from cdn.example.com and another allows scripts from api.example.com, neither source is permitted because each is blocked by the other policy. This commonly happens when a CDN, load balancer, or application framework each add their own CSP header independently. Each policy is correct in isolation, but the intersection blocks resources that every individual policy intended to allow. Debugging is difficult because each header appears valid when examined alone.

Deprecated directives: X-WebKit-CSP and X-Content-Security-Policy

Early CSP implementations used vendor-prefixed headers: X-WebKit-CSP for WebKit browsers and X-Content-Security-Policy for Firefox and Internet Explorer. These headers were experimental and have been deprecated since CSP Level 2. Modern browsers ignore them entirely. A site sending only the deprecated header name has no active CSP in any current browser. This often persists in infrastructure configuration files, server templates, or middleware that has not been updated. The header appears in response inspection, giving the impression of active policy, while the browser silently discards it.

HTTP sources in HTTPS context

A Content Security Policy served over HTTPS that includes HTTP sources in its directives undermines the encryption guarantee. If script-src includes http://cdn.example.com, a network attacker can intercept and modify scripts loaded from that origin. The browser permits the load because the CSP explicitly allows it. Mixed content protections that would normally block HTTP resources on HTTPS pages are overridden by the explicit policy declaration. The site invested in HTTPS deployment but the CSP re-opens the exact attack vector that HTTPS was designed to close. Any directive that references an HTTP origin on an HTTPS page creates this condition: script-src, style-src, img-src, connect-src, and others.

The correct change

Audit the Content-Security-Policy header for each of these conditions. For meaningless policies, add fetch directives that restrict script-src, default-src, and other resource types to specific trusted origins. For multiple headers, consolidate to a single CSP header at the infrastructure level, ensuring only one layer sets the header. For deprecated headers, replace X-WebKit-CSP and X-Content-Security-Policy with Content-Security-Policy. For HTTP sources, replace all http:// origins with https:// equivalents or use protocol-relative references. The end state is a single, enforced Content-Security-Policy header using the standard header name, containing fetch directives that restrict resources to specific HTTPS origins.

Scope

This condition applies globally. CSP configuration typically originates from server configuration, application middleware, or CDN settings that affect all responses. Multiple header issues specifically arise from layered infrastructure where more than one system contributes headers. Each layer must be audited independently and the final response verified as a whole.

How to verify

  • Validation confirms the condition is resolved:
  • • Response contains exactly one Content-Security-Policy header
  • • No X-WebKit-CSP or X-Content-Security-Policy headers present
  • • Policy includes at least default-src or script-src with restrictive values
  • • No unsafe-inline, unsafe-eval, or wildcard sources unless specifically justified
  • • All source origins use HTTPS, no HTTP origins in any directive
  • • No Content-Security-Policy-Report-Only used as the sole policy
  • • Browser developer tools confirm CSP is active and blocking test violations
  • • Response headers inspected at the final delivery point, not just application layer

Takeaway

  • A CSP header without fetch directives restricts nothing while signaling security to auditors
  • Multiple CSP headers use intersection semantics, blocking resources that any individual policy allows
  • Deprecated header names are silently ignored by every modern browser
  • HTTP sources in HTTPS policies re-open the exact attack surface that encryption was meant to close
  • A misconfigured CSP is worse than no CSP because it suppresses the signals that would otherwise surface the gap

FAQ

How do I detect if my CSP is meaningless?

Examine the policy for fetch directives: default-src, script-src, style-src, img-src, connect-src, font-src, object-src, media-src, frame-src, child-src, worker-src, and manifest-src. If none of these appear, the policy does not restrict resource loading. If they appear but use wildcard values with unsafe-inline and unsafe-eval, the restrictions are effectively absent. A useful CSP must restrict at least script-src or default-src to specific origins.

Why does having two CSP headers block more than either one alone?

The CSP specification requires browsers to enforce all policies independently. A resource must satisfy every policy to load. If Policy A allows cdn.example.com for scripts and Policy B allows api.example.com for scripts, neither source appears in both policies, so both are blocked. This is intersection, not union. The behavior is specified in CSP Level 2 and is consistent across browsers. It is the single most common source of unexpected CSP breakage in production.

Are deprecated CSP headers ever useful for backward compatibility?

In practice, no. The browsers that supported X-WebKit-CSP and X-Content-Security-Policy have been updated to support the standard Content-Security-Policy header for years. The vendor-prefixed headers apply to browser versions that represent negligible traffic share. Maintaining deprecated headers adds configuration complexity without meaningful coverage. Remove them and use the standard header exclusively.

Can I use protocol-relative URLs in CSP directives instead of specifying HTTPS?

CSP does not support protocol-relative URLs in source expressions. Specify the full origin with https:// prefix. Alternatively, omit the protocol to match both HTTP and HTTPS, but this re-introduces the mixed content risk. The correct approach is to explicitly list https:// origins for every source. If a resource is only available over HTTP, that resource cannot be safely included in an HTTPS page regardless of CSP configuration.

How do I find which layer is adding extra CSP headers?

Inspect headers at each infrastructure layer: application server, reverse proxy, CDN, and load balancer. Compare the CSP header at the application response with the header received by the browser. Each layer that adds, modifies, or duplicates the header must be identified. Use the browser network inspector to see the final headers, then trace backward through infrastructure. Common culprits include CDN security features, web application firewalls, and framework middleware that each add their own policy independently.

Does a meta tag CSP interact with header CSP through intersection?

Yes. A Content-Security-Policy meta tag and a Content-Security-Policy HTTP header are treated as separate policies. The browser applies intersection semantics to both. This is another source of unexpected blocking. If the meta tag and header contain different source lists, resources must satisfy both. Additionally, meta tags cannot set frame-ancestors, report-uri, or sandbox directives. Consolidating policy into the HTTP header avoids this interaction entirely.

The fix

Eliminate CSP configurations that create false confidence by restricting nothing, conflicting with each other, using deprecated syntax, or undermining HTTPS

Done when: A single Content-Security-Policy header using the standard header name, containing fetch directives that restrict resources to specific HTTPS origins, with no deprecated headers, no duplicate policies, and no HTTP sources

Do not

  • Do not assume a present CSP header means active protection without verifying directive content
  • Do not add CSP headers at multiple infrastructure layers without consolidation
  • Do not maintain deprecated X-WebKit-CSP or X-Content-Security-Policy headers
  • Do not include HTTP origins in policies served over HTTPS

How to verify

  • Exactly one Content-Security-Policy header in each response
  • No deprecated CSP header names present
  • Policy contains restrictive fetch directives
  • All source origins use HTTPS
  • Browser developer tools confirm active enforcement
  • Test violations are blocked, not just reported

Find every page with this

probeo monitors this continuously across every page of your site, and records which pages passed, which failed, and which it could not determine.

Book a Call