Dev.to Security 🔐 Cybersecurity 👁 0 📖 4 min read

Missing Content-Security-Policy: What It Means and What to Check Next

A lot of public sites still ship pages without an enforced Content-Security-Policy response header. Scanners flag that as “Missing Content-Security-Policy header.” If the site looks fine, why care? CSP tells the browser

A lot of public sites still ship pages without an enforced Content-Security-Policy response header. Scanners flag that as “Missing Content-Security-Policy header.” If the site looks fine, why care?

CSP tells the browser which scripts, styles, frames, and other resources a page may load. MDN describes its main job as limiting where JavaScript can come from, which makes many XSS bugs harder to exploit. OWASP treats it as defense in depth on top of proper input handling, not a replacement for it.

A missing enforced header is a gap in that second layer. It is not proof that someone can already run script in your users’ browsers.

1. Read the header the way a visitor would

Prefer GET over HEAD; some stacks answer them differently. Follow redirects so you inspect the final URL.

curl -sL -D csp-headers.txt -o csp-body.html -w 'final_url=%{url_effective}\n' https://example.com/
head -1 csp-headers.txt
grep -i '^content-security-policy:' csp-headers.txt
grep -i '^content-security-policy-report-only:' csp-headers.txt

-L follows redirects. For multi-hop responses, read the last header block in csp-headers.txt. Confirm final_url is the document visitors see.

Sample output and how to read it

Case A — no enforced CSP in headers:

final_url=https://example.com/
HTTP/2 200
content-type: text/html; charset=utf-8

grep for ^content-security-policy: returns nothing. That means no enforced policy was found in the response headers; also check the HTML for <meta http-equiv="Content-Security-Policy">. Meta delivery does not support every directive (including frame-ancestors and reporting), so prefer the HTTP header when you can (OWASP WSTG-CONF-12).

Case B — Report-Only only:

content-security-policy-report-only: default-src 'self'; script-src 'self'

Violations can be logged. The browser does not block them under this header. Review what would be blocked; Report-Only itself does not break the site.

Case C — enforced but soft:

content-security-policy: default-src *; script-src * 'unsafe-inline' 'unsafe-eval'

A header is present, so a “missing CSP” finding may clear, but script-src still allows almost anything. Presence is not the same as a strict policy.

This check only sees that URL today. Login and account paths often differ from the marketing homepage.

2. Check a page that changes state

for path in / /login /account /settings; do
  printf '%s ' "$path"
  curl -sL -o /tmp/csp-body -D - -w ' final=%{url_effective}\n' "https://example.com$path" \
    | grep -iE '^(HTTP/|content-security-policy:|content-security-policy-report-only:|location:)'
done

CSP on / and nothing on /account is a gap where sessions live.

3. Present is not the same as strict

If a header exists, split on ; and ask:

  • Does script-src or default-src include 'unsafe-inline' without a nonce or hash?
  • Does it allow 'unsafe-eval'?
  • Is there a bare * for scripts?
  • Is base-uri constrained? Strict examples often set base-uri 'none'.
  • Is frame-ancestors set if you care about framing?

A missing object-src alone is not proof of weakness: if it is omitted, it can fall back to default-src. Strict examples often set object-src 'none' when plugins are not needed, but absence by itself is not a finding.

OWASP’s cheat sheet steers teams toward nonce- or hash-based “strict” CSP (optionally with strict-dynamic), not a sprawling third-party allowlist.

4. Report-Only first, then enforce

Ship Content-Security-Policy-Report-Only, review what would be blocked in the console and reports, then move the same policy to Content-Security-Policy. MDN notes both headers can coexist. Do not leave Report-Only forever.

What a clean result does not mean

A sensible CSP on the pages you tested does not prove the app has no XSS, that third-party scripts are safe, or that cookies and auth are fine. Outside checks see only what the server returns.

A basic starting example (not a ready setting)

OWASP’s basic same-origin example:

Content-Security-Policy: default-src 'self'; frame-ancestors 'self'; form-action 'self';

This is only a starting point. It is not a ready setting for every site, it may block existing scripts or embeds, and it is not a Strict CSP. Test it first as Content-Security-Policy-Report-Only on staging. Then move toward nonce- or hash-based script-src, object-src 'none', and base-uri 'none'.

About us

We build HackerMe, an automated website security scanner by Agura / OMC Group. Security headers are part of every website scan we run (a sample finding on our public pages is “Missing Content-Security-Policy header”); deeper CSP analysis can check policy quality on Standard and higher scan types. Results are a starting point, not a claim that a site is safe. HackerMe offers a free scan on a domain you own or have explicit permission to test: https://brandingme.ai/app/r/7FjzRLWEPg6mzUJGpq536g

Sources

📰 Read the original article on Dev.to Security

Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.