Documentation

CSP Violations

Configure Content Security Policy reporting to detect XSS attempts, unauthorized script execution, and policy misconfigurations in real-time.

New to CSP monitoring? Read our CSP Violations overview to understand the benefits before diving into configuration.

What is Content Security Policy?

Content Security Policy (CSP) is a browser security standard that lets you control which resources (scripts, styles, images, fonts, etc.) can load on your web pages. By defining an allowlist of trusted sources, CSP prevents cross-site scripting (XSS) attacks and other code injection vulnerabilities.

CSP works through HTTP headers. You send a Content-Security-Policy header with your server responses, and browsers automatically enforce your rules. When a resource violates your policy, the browser blocks it and can send a violation report to your configured endpoint.

The policy uses directives to control different resource types: script-src for JavaScript, style-src for CSS, img-src for images, and so on. Each directive specifies which origins are allowed to provide that resource type.

Specification

Browser Support

CSP has ~93% global browser support, making it one of the most widely supported security features.

Browser CSP Support Reporting
Chrome 14+ report-to (70+) and report-uri
Edge 12+ report-to (79+) and report-uri
Safari 6+ report-to (16.4+) and report-uri
Firefox 4+ report-uri only (no report-to)
Warning: Firefox does not support the modern report-to directive. Use both report-to and report-uri for maximum browser coverage.

Why Monitor CSP Violations?

CSP violations reveal critical security information that's otherwise invisible to your monitoring stack:

  • Detect XSS attacks — Blocked script injections indicate attempted attacks on your users
  • Discover policy misconfigurations — Legitimate features may be blocked by overly strict policies
  • Monitor third-party scripts — Track when external scripts attempt to load unauthorized resources
  • Meet compliance requirements — PCI DSS 4.0 mandates script monitoring on payment pages
  • Safely test policy changes — Report-Only mode lets you preview the impact of stricter policies

Who Benefits

  • Security teams — Gain visibility into client-side attack attempts
  • Compliance officers — Maintain audit trails for PCI DSS 4.0, SOC 2, and similar standards
  • Frontend architects — Validate CSP policies before enforcement and catch regressions

When to Enable

CSP reporting is recommended for all production websites. Start with Content-Security-Policy-Report-Only to collect data without blocking resources. Once you've refined your policy, switch to enforcement mode while keeping reporting enabled for ongoing monitoring.

How to Configure

Step 1: Set Up the Reporting Endpoint

First, define where browsers should send violation reports using the Reporting-Endpoints header:

HTTP Define the reporting endpoint
Reporting-Endpoints: default="https://reporting-api.app/browser-reports/YOUR-ENDPOINT-UUID"

Replace YOUR-ENDPOINT-UUID with your application's unique endpoint from the reporting-api.app dashboard.

Step 2: Enable Report-Only Mode

Before enforcing your policy, test it using the Content-Security-Policy-Report-Only header. This sends violation reports without actually blocking resources:

HTTP Report-Only mode for safe testing
Content-Security-Policy-Report-Only: default-src 'self'; script-src 'self'; style-src 'self'; report-to default

Report-Only mode is essential for:

  • Testing new policies against production traffic
  • Identifying legitimate resources that would be blocked
  • Gradually tightening security without breaking features

Step 3: Configure Policy Directives

CSP policies use directives to control resource loading. Here are common configurations:

Basic Policy

HTTP Minimal CSP (allow same-origin resources)
Content-Security-Policy: default-src 'self'; report-to default

Moderate Policy

HTTP Typical production policy
Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.example.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' https://fonts.gstatic.com; connect-src 'self' https://api.example.com; frame-ancestors 'self'; report-to default

Strict Policy (Nonce-Based)

HTTP Strict CSP with nonces (recommended)
Content-Security-Policy: default-src 'self'; script-src 'nonce-{RANDOM}' 'strict-dynamic'; style-src 'self' 'nonce-{RANDOM}'; object-src 'none'; base-uri 'none'; report-to default

Key directives:

  • default-src — Fallback for all resource types
  • script-src — Controls JavaScript loading
  • style-src — Controls CSS loading
  • img-src — Controls image sources
  • connect-src — Controls fetch, XHR, WebSocket connections
  • frame-ancestors — Prevents clickjacking (replaces X-Frame-Options)

Step 4: Monitor Reports and Refine

After deploying Report-Only mode, monitor incoming reports in your dashboard. Common findings include:

  • Browser extension violations — Extensions inject scripts from chrome-extension://, moz-extension://, etc. These are false positives from user-installed software.
  • Inline script violations — If your app uses inline <script> tags, you'll need to add nonces or hashes.
  • Third-party script violations — Analytics, ads, and widgets may load additional resources. Whitelist legitimate ones.

Iteratively refine your policy based on reports until violations represent either attack attempts or known false positives.

Step 5: Enable Enforcement

Once you're confident in your policy, switch from Report-Only to enforcing mode:

HTTP Enforcing CSP with continued reporting
Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self'; report-to default
Best practice: Keep report-to enabled even after enforcement. This provides ongoing visibility into blocked attacks and catches regressions when your application changes.

Step 6: Set Up Integrations

Route CSP violation reports to your existing tools for alerting and analysis:

See the Integrations documentation for setup instructions.

Understanding Violation Reports

When a CSP violation occurs, the browser sends a JSON report to your endpoint. Understanding this payload helps you identify threats and fix policy issues.

CSP violation report detail view showing blocked URL, directive, and source location
A CSP violation report in the dashboard showing key fields

Report Fields

Field Description
blockedURL URL of the blocked resource, or "inline" for inline scripts/styles
effectiveDirective The specific CSP directive that was violated (e.g., script-src-elem)
disposition "enforce" if the resource was blocked, "report" if Report-Only mode
sample First 40 characters of blocked inline code (useful for identifying XSS attempts)
sourceFile URL of the file containing the violation
lineNumber, columnNumber Exact location in the source file where the violation occurred
documentURL URL of the page where the violation was triggered
originalPolicy The full CSP header that was violated

Example Report

JSON CSP violation report (inline script blocked)
{
  "type": "csp-violation",
  "url": "https://example.com/checkout",
  "body": {
    "blockedURL": "inline",
    "effectiveDirective": "script-src-elem",
    "disposition": "enforce",
    "sample": "console.log(\"injected code\")",
    "sourceFile": "https://example.com/checkout",
    "lineNumber": 42,
    "columnNumber": 1,
    "documentURL": "https://example.com/checkout",
    "originalPolicy": "default-src 'self'; script-src 'self'; report-to default"
  }
}

How to Act on Findings

  • blockedURL: "inline" — An inline script or style was blocked. Consider refactoring to external files, or add nonces/hashes if inline code is necessary.
  • blockedURL: external domain — A resource from an unauthorized domain was blocked. If it's a legitimate third-party service, add it to your allowlist. If unexpected, investigate as a potential attack.
  • disposition: "report" — The resource wasn't actually blocked (Report-Only mode). Safe to analyze and refine your policy before enforcement.
  • High variety in sample field — Many different blocked code snippets may indicate XSS attack attempts. Review for patterns like <script> injection or encoded payloads.

HTML Meta Tag Alternative

CSP can also be set via an HTML <meta> tag:

HTML CSP via meta tag
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'">

Limitations of meta tags:

  • frame-ancestors directive is not supported (use HTTP header for clickjacking protection)
  • report-to and report-uri are not supported (no violation reporting)
  • sandbox directive is not supported
  • Must appear early in <head> before any resources load

Recommendation: Use HTTP headers for production CSP. Meta tags are useful for testing or when you don't control server headers, but they cannot send violation reports.

Firefox Compatibility

Firefox does not support the modern report-to directive and Reporting-Endpoints header. For maximum browser coverage (~96%), use both reporting methods:

HTTP Combined headers for all browsers
Reporting-Endpoints: default="https://reporting-api.app/browser-reports/YOUR-UUID"
Content-Security-Policy: default-src 'self'; script-src 'self'; report-to default; report-uri https://reporting-api.app/browser-reports/YOUR-UUID

Modern browsers (Chrome, Edge, Safari) use report-to and ignore report-uri. Firefox uses report-uri. reporting-api.app handles both formats at the same endpoint.

Common Violation Types

Directive Common Causes
script-src-elem Inline scripts, unauthorized external scripts, browser extensions
style-src-elem Inline styles, unauthorized stylesheets, browser extensions
img-src Images from unauthorized domains, data URIs, tracking pixels
connect-src Fetch/XHR requests to unauthorized APIs, analytics beacons
frame-ancestors Page embedded in unauthorized iframe (clickjacking attempt)

Server Configuration Examples

Nginx

Nginx nginx.conf
server {
    # ... your existing configuration ...

    # Reporting endpoint
    add_header Reporting-Endpoints 'default="https://reporting-api.app/browser-reports/YOUR-UUID"' always;

    # CSP with both reporting methods
    add_header Content-Security-Policy "default-src 'self'; script-src 'self'; report-to default; report-uri https://reporting-api.app/browser-reports/YOUR-UUID" always;
}

Ruby on Rails

Ruby config/initializers/content_security_policy.rb
Rails.application.config.content_security_policy do |policy|
  policy.default_src :self
  policy.script_src  :self
  policy.style_src   :self
  policy.img_src     :self, :data

  # Legacy report-uri for Firefox
  policy.report_uri "https://reporting-api.app/browser-reports/YOUR-UUID"
end

# Add Reporting-Endpoints header for modern browsers
# Note: Use lowercase header names for Rack 3 compatibility
Rails.application.config.action_dispatch.default_headers.merge!(
  "reporting-endpoints" => 'default="https://reporting-api.app/browser-reports/YOUR-UUID"'
)

Note: Rails doesn't natively support the report-to directive yet. The configuration above uses report-uri for the CSP header. Modern browsers will use the Reporting-Endpoints header automatically when report-to default is specified in your policy.

Troubleshooting

Reports Not Appearing

  • Check allowed origins — Ensure your website's origin is whitelisted in your application settings. Reports from unlisted origins are rejected.
  • Verify headers are sent — Use browser DevTools (Network tab) to confirm Reporting-Endpoints and Content-Security-Policy headers are present.
  • Wait for batching — Browsers may batch reports for up to 60 seconds before sending. Be patient after triggering a test violation.
  • Check browser support — If testing with Firefox, ensure you have report-uri configured (Firefox doesn't support report-to).

Too Many False Positives

  • Browser extensions — Extensions inject scripts from chrome-extension://, moz-extension://, and safari-extension:// URIs. These are not security threats. reporting-api.app can filter these automatically.
  • ISP-injected content — Some ISPs inject scripts for analytics or ads. These appear as violations from unknown domains.
  • Antivirus software — Desktop antivirus may inject scripts to scan page content.

Policy Breaking Legitimate Features

  • Use Report-Only first — Always test with Content-Security-Policy-Report-Only before enforcement.
  • Check the blockedURL — Reports show exactly which resource was blocked. Add it to your allowlist if legitimate.
  • Review inline code — If blockedURL is "inline", you need to refactor or add nonces/hashes.

Next Steps