Skip to content

Troubleshooting

Solutions for common CSP configuration issues.

This is the most common cause, and the only one with no local symptom. Content blockers — uBlock Origin, Adblock Plus, Brave Shields, Privacy Badger and others — block the browser’s POST to any reporting endpoint. There is no console error, no CSP violation, and nothing in DevTools that reads as a failure to a non-expert. Reports simply never arrive, and a correctly configured site looks identical to one that has had no violations.

To confirm it:

  1. Open DevTools and switch to the Network tab.
  2. Reload the page and trigger a violation.
  3. Look for a request to ingest.headerhawk.com. If it is missing, or shows as cancelled or blocked, an extension is the cause.
  4. Disable the extension for your origin and reload. Reports should appear within a minute.

Reports are matched against the domain registered for the site, exactly. A site registered as example.com accepts reports from example.com and discards reports from www.example.com, app.example.com, and every other subdomain.

This matters most when one CSP header is deployed across several hosts, which is the normal case. The endpoint returns the same success status whether a report was stored or discarded — deliberately, so that the response cannot be used to discover which domains a tenant monitors — so nothing in the browser shows a problem.

To check, go to Settings → Sites, open the site’s row menu and choose View Setup. If reports have been discarded for a domain mismatch, the panel lists how many and which hosts they came from. If a listed host is one of yours, add it as its own site to start collecting its reports.

Verify the CSP header is being sent:

Terminal window
curl -I https://your-site.com | grep -i content-security-policy

You should see either Content-Security-Policy or Content-Security-Policy-Report-Only.

Ensure your report-uri directive is correct:

report-uri https://ingest.headerhawk.com/csp/YOUR_INGEST_CODE

Common mistakes:

  • Missing https://
  • Wrong ingest code
  • Typo in the domain name

Send a test report. Use your own registered domain as the document-uri — a report whose document-uri does not match the site’s domain is discarded, and the response looks identical either way:

Terminal window
curl -i -X POST \
https://ingest.headerhawk.com/csp/YOUR_INGEST_CODE \
-H "Content-Type: application/csp-report" \
-d '{"csp-report":{"document-uri":"https://your-site.com/","violated-directive":"script-src","original-policy":"default-src self"}}'

Other statuses do tell you something:

Status Meaning What to do
404 Nothing is accepting reports for that ingest code: either no site has it, or the site it belongs to is deactivated Check the code against the site’s setup panel, then check the site is active
400 The path is not /csp/{8-character code}, or the report body failed validation Check the URL shape, and that the body has document-uri, violated-directive and original-policy
429 Rate limited, or the tenant is past its monthly hard quota Back off, or check usage in the dashboard

404 covers both the unknown-code and the deactivated-site case, and does not say which. That is deliberate, for the same reason a domain-mismatch drop still returns 204: the ingest code sits in the report-uri of every page that uses it, so it is public by construction, and a status that distinguished “real code” from “no such code” would let anyone work through the 8-character keyspace and confirm which codes belong to real customers. Both of your own cases are visible from the dashboard, where the ambiguity costs you nothing.

Check the browser console for CSP messages. Violations appear as warnings even in report-only mode.

Script integrity reporting ('report-sha256') has its own set of silences, separate from violation reporting. This section is the diagnostic short list; Script Integrity Monitoring explains the mechanism in full — what the data covers, how sampling works, and how to read the Origins, Scripts and Pages views.

It needs the Reporting API, not report-uri

Section titled “It needs the Reporting API, not report-uri”

A hash report is a Reporting API report type. report-uri predates report types and carries CSP violation reports only, so it has no way to deliver a hash report. A policy combining 'report-sha256' with report-uri looks correct and reports nothing.

Use the Reporting-Endpoints header with report-to:

Reporting-Endpoints: csp-endpoint="https://ingest.headerhawk.com/csp/YOUR_INGEST_CODE"
Content-Security-Policy-Report-Only: script-src 'self' 'report-sha256'; report-to csp-endpoint

Note the quotes. 'report-sha256' is a keyword source expression and must be quoted and placed inside script-src; bare report-sha256 is parsed as a hostname and does nothing.

The browser generates a hash when it fetches and executes a script. On a normal reload an external script is usually served from cache, no fetch happens, and no hash is generated — however correct your policy is. A hard refresh (shift+reload) forces the fetch, which removes caching as an explanation; it is not a guarantee that a report will follow, since support is still narrow (see below).

The same effect applies to real traffic: a site with well-cached assets reports far fewer hashes than it has scripts, and returning visitors contribute almost none. Combined with the sample rate, treat the Scripts page as a sample of your scripts rather than a complete inventory.

Chromium 133+ implements report-sha256; WebKit support is in progress; Firefox does not support it. Some Chromium builds accept the keyword and emit no hash reports at all. An empty Scripts page is not by itself evidence of a misconfiguration — see Browser support is narrow for what has actually been observed.

Always use Content-Security-Policy-Report-Only first:

Content-Security-Policy-Report-Only: default-src 'self'; report-uri https://ingest.headerhawk.com/csp/YOUR_INGEST_CODE

This reports violations without blocking anything.

Issue Solution
Inline scripts blocked Add 'unsafe-inline' or use nonces
CDN scripts blocked Add the CDN domain to script-src
Google Fonts blocked Add fonts.googleapis.com and fonts.gstatic.com
Images not loading Add domains to img-src or use https:
API calls failing Add API domain to connect-src
  1. Open browser DevTools
  2. Go to the Console tab
  3. Look for “Refused to…” messages
  4. Note the blocked URL and violated directive

Some violations are expected or from browser extensions. Filter in HeaderHawk dashboard by:

  • Document host (your domains only)
  • Blocked host (exclude known extension domains)
  • Directive (focus on critical directives)
Source Cause Action
chrome-extension:// Browser extensions Filter out in dashboard
moz-extension:// Firefox extensions Filter out in dashboard
about:blank Injected iframes Usually safe to ignore
localhost Dev tools or extensions Filter out in dashboard
  1. Run in report-only mode for at least a week
  2. Review all violation types in the dashboard
  3. Add legitimate resources to your policy
  4. Test critical user flows
  5. Have a rollback plan

Change the header from:

Content-Security-Policy-Report-Only: ...

To:

Content-Security-Policy: ...

Keep the report-uri to continue receiving reports about enforced blocks.

If things break:

  1. Immediately revert to Content-Security-Policy-Report-Only
  2. Check new violations in the dashboard
  3. Update policy to allow legitimate resources
  4. Try enforcing again

Problem: Refused to execute inline script

Solutions:

  1. Move to external file (recommended)
  2. Use nonce:
    Content-Security-Policy: script-src 'nonce-abc123'
    <script nonce="abc123">
    ...
    </script>
  3. Use hash:
    Content-Security-Policy: script-src 'sha256-...'
  4. Allow unsafe-inline (not recommended):
    Content-Security-Policy: script-src 'unsafe-inline'

Problem: Refused to apply inline style

Solutions:

  1. Move styles to external CSS file
  2. Use 'unsafe-inline' for styles (lower risk than scripts):
    Content-Security-Policy: style-src 'self' 'unsafe-inline'

Problem: Refused to evaluate a string as JavaScript

Cause: Your code or a library uses eval(), new Function(), or similar.

Solutions:

  1. Replace with safer alternatives
  2. If unavoidable, add 'unsafe-eval' (security risk):
    Content-Security-Policy: script-src 'unsafe-eval'

Problem: WebSocket connection refused

Solution: Add to connect-src:

Content-Security-Policy: connect-src 'self' wss://your-websocket-server.com

Problem: Cannot load content in iframe

Solution: Add source to frame-src:

Content-Security-Policy: frame-src 'self' https://youtube.com https://player.vimeo.com

If you’re stuck:

  1. Check the browser console for specific error messages
  2. Review violations in the HeaderHawk dashboard
  3. Search for the specific directive and error message
  4. Test with a minimal policy and add directives incrementally