Home/Blog/What is CSP report-only mode?
Web Security

What is CSP report-only mode?

Learn how to use Content Security Policy report-only mode to test and validate CSP rules without blocking content, minimizing user impact during implementation.

By Inventive HQ Team
What is CSP report-only mode?

Understanding CSP Report-Only Mode

Content Security Policy (CSP) is a powerful security mechanism, but it can also break website functionality if not properly configured. CSP report-only mode allows you to test and refine your CSP rules before enforcing them, preventing unintended content blocking.

CSP has two operational modes: enforcement mode (Content-Security-Policy header) and report-only mode (Content-Security-Policy-Report-Only header). Report-only mode monitors policy violations and reports them without actually blocking resources.

How Report-Only Mode Works

When you use report-only mode, the browser doesn't block any resources that violate the policy. Instead, the browser reports the violations through the reporting mechanism you've configured.

The main use case is testing: you can deploy a CSP in report-only mode, monitor violations, refine the policy, and only switch to enforcement when confident the policy won't break functionality.

Comparison: Enforcement vs Report-Only

Enforcement Mode (Content-Security-Policy):

  • Blocks resources that violate the policy
  • Users experience content loss if policy is too strict
  • Risk of breaking website functionality
  • Most restrictive

Report-Only Mode (Content-Security-Policy-Report-Only):

  • Allows all resources
  • Reports violations
  • No user-facing impact
  • Safe for testing

Implementing Report-Only Mode

Setting the Header

Content-Security-Policy-Report-Only: default-src 'self'; script-src 'self' 'unsafe-inline'; report-uri /csp-report

In various server technologies:

Express.js:

const helmet = require('helmet');
const express = require('express');
const app = express();

app.use(helmet.contentSecurityPolicy({
  directives: {
    defaultSrc: ["'self'"],
    scriptSrc: ["'self'", "'unsafe-inline'"],
    styleSrc: ["'self'"],
    imgSrc: ["'self'", "https:"],
    reportUri: "/csp-report"
  },
  reportOnly: true  // Report-only mode
}));

Nginx:

add_header Content-Security-Policy-Report-Only "default-src 'self'; script-src 'self' https:; report-uri /csp-report;";

Apache:

Header add Content-Security-Policy-Report-Only "default-src 'self'; script-src 'self' https:; report-uri /csp-report;"

Setting Up Violation Reporting

Report-only mode is most valuable when you capture and analyze violation reports.

Basic Reporting with report-uri

Specify where violations should be reported:

Content-Security-Policy-Report-Only:
  default-src 'self';
  script-src 'self' 'unsafe-inline';
  report-uri /csp-report

When a violation occurs, the browser POSTs a JSON report to /csp-report:

{
  "csp-report": {
    "document-uri": "https://example.com/page",
    "violated-directive": "script-src",
    "effective-directive": "script-src",
    "original-policy": "default-src 'self'; script-src 'self' 'unsafe-inline'; report-uri /csp-report",
    "disposition": "report",
    "blocked-uri": "https://cdn.example.com/analytics.js",
    "status-code": 200
  }
}

Handling CSP Reports

Express.js endpoint:

app.post('/csp-report', express.json({ type: 'application/csp-report' }), (req, res) => {
  const violation = req.body['csp-report'];

  console.log('CSP Violation Report:', {
    documentUri: violation['document-uri'],
    violatedDirective: violation['violated-directive'],
    blockedUri: violation['blocked-uri']
  });

  // Store violations in database for analysis
  CSPViolation.create({
    documentUri: violation['document-uri'],
    violatedDirective: violation['violated-directive'],
    blockedUri: violation['blocked-uri'],
    originalPolicy: violation['original-policy'],
    timestamp: new Date()
  });

  res.status(204).send(); // No content response
});

Third-Party Reporting Services

Rather than implementing your own reporting, use services like:

  • Report-uri.com: Dedicated CSP reporting service
  • Sentry: Error tracking with CSP support
  • Bugsnag: Crash reporting with CSP reporting

Report-uri.com example:

Content-Security-Policy-Report-Only:
  default-src 'self';
  report-uri https://yourname.report-uri.com/r/d/csp/reportOnly

Testing Workflow

Implementing CSP effectively follows a structured approach:

Step 1: Deploy Lenient Report-Only Policy

Start with a lenient policy in report-only mode:

Content-Security-Policy-Report-Only:
  default-src 'self';
  script-src 'self' 'unsafe-inline' https:;
  style-src 'self' 'unsafe-inline' https:;
  report-uri /csp-report

This policy is permissive but still identifies major issues.

Step 2: Monitor Violations

Collect violation reports for a period (days or weeks depending on traffic):

// Count violations by type
SELECT violated_directive, COUNT(*) as count
FROM csp_violations
GROUP BY violated_directive
ORDER BY count DESC;

// Identify problematic resources
SELECT blocked_uri, COUNT(*) as count
FROM csp_violations
WHERE violated_directive = 'script-src'
GROUP BY blocked_uri
ORDER BY count DESC;

Step 3: Whitelist Necessary Resources

Based on violation reports, add trusted resources to your policy:

Content-Security-Policy-Report-Only:
  default-src 'self';
  script-src 'self' https://cdn.example.com https://analytics.google.com;
  style-src 'self' https://fonts.googleapis.com;
  report-uri /csp-report

Step 4: Tighten the Policy

Remove overly permissive directives:

Content-Security-Policy-Report-Only:
  default-src 'self';
  script-src 'self' https://cdn.example.com https://analytics.google.com;
  style-src 'self' https://fonts.googleapis.com;
  img-src 'self' https:;
  report-uri /csp-report

Step 5: Switch to Enforcement

Once violations are resolved, switch to enforcement mode:

Content-Security-Policy:
  default-src 'self';
  script-src 'self' https://cdn.example.com https://analytics.google.com;
  style-src 'self' https://fonts.googleapis.com;
  img-src 'self' https:;
  report-uri /csp-report

Note: Use both headers during transition for additional safety.

Running Both Headers Simultaneously

Best practice during transition is running both headers:

Content-Security-Policy: [enforcement policy]
Content-Security-Policy-Report-Only: [stricter policy for testing]

This approach:

  • Enforces your current policy
  • Tests a stricter policy in report-only mode
  • Identifies issues before enforcement
  • Allows gradual policy tightening

Example transition:

# Current enforced policy (permissive)
Content-Security-Policy:
  default-src 'self';
  script-src 'self' 'unsafe-inline' https:

# Testing stricter policy (report-only)
Content-Security-Policy-Report-Only:
  default-src 'self';
  script-src 'self' https://trusted-cdn.com;
  report-uri /csp-report

Users experience the enforced policy, while the stricter policy is tested safely.

Common Report-Only Patterns

Initial Exploration

Content-Security-Policy-Report-Only:
  default-src 'self' https:;
  report-uri /csp-report

Very permissive, identifies major issues.

Testing Stricter Version

Content-Security-Policy-Report-Only:
  default-src 'self';
  script-src 'self' https://cdn.example.com;
  style-src 'self' https://fonts.googleapis.com;
  report-uri /csp-report

More restrictive, tests specific resource whitelisting.

Testing No Inline Scripts

Content-Security-Policy-Report-Only:
  default-src 'self';
  script-src 'self';
  style-src 'self' https:;
  report-uri /csp-report

Very strict, identifies inline script dependencies.

Analyzing Violation Reports

Common Violations and Solutions

Inline Scripts:

{
  "violated-directive": "script-src",
  "blocked-uri": "inline"
}

Solution: Remove inline scripts or use nonces/hashes:

<script nonce="random-nonce">
  console.log('Allowed');
</script>

Third-Party Analytics:

{
  "violated-directive": "script-src",
  "blocked-uri": "https://analytics.google.com/analytics.js"
}

Solution: Whitelist analytics domain:

script-src 'self' https://analytics.google.com

Google Fonts:

{
  "violated-directive": "font-src",
  "blocked-uri": "https://fonts.gstatic.com/s/..."
}

Solution: Add font-src directive:

font-src 'self' https://fonts.gstatic.com

Best Practices for Report-Only Mode

  1. Start permissive: Begin with lenient policies to identify what needs whitelisting
  2. Monitor consistently: Regularly review violation reports
  3. Use reporting service: Don't handle raw reports; use dedicated services
  4. Set time limits: Plan specific periods for testing (e.g., 2 weeks)
  5. Iterate gradually: Tighten policies incrementally
  6. Document decisions: Explain why each domain is whitelisted
  7. Test cross-browser: Violations may vary by browser
  8. Archive reports: Keep historical violation data for analysis

Production Recommendations

Once in production:

  • Monitor violation reports continuously
  • Alert on unexpected new violations (potential attacks)
  • Maintain both enforcement and report-only during testing phases
  • Have a response plan for CSP violation patterns
  • Regularly review and tighten CSP

Limitations of Report-Only Mode

  • Doesn't provide actual protection (only monitoring)
  • Report delivery isn't guaranteed (can be blocked by network)
  • Some browsers have CSP report limitations
  • Report-only adds header size overhead
  • Requires active monitoring to be valuable

Report-only mode is an essential tool for safely implementing Content Security Policy. By using it to test policies before enforcement, you can significantly reduce the risk of accidentally breaking website functionality while still improving security. The key is active monitoring and iterative refinement based on violation reports.

Need Expert IT & Security Guidance?

Our team is ready to help protect and optimize your business technology infrastructure.