Content Security Policy
content security policy content security policy restricts html-loaded script and resource origins — owas content security policy (csp) is an
Introduction
Content Security Policy (CSP) is an HTTP header (or limited meta tag) instructing browsers which script, style, image, and frame sources are permitted — OWASP's primary XSS mitigation when encoding fails. Staff engineers deploy nonce-based strict CSP with violation reporting, balancing security against analytics and legacy inline markup.
Business problem
XSS without CSP runs attacker script with full page access. Marketing adds inline GTM snippet — breaks strict CSP unless nonce pipeline integrated. Report-only mode exists to tune policy before enforcement blocks revenue tags.
- PCI: Payment pages often mandate strict script-src.
- Violations: Unmonitored report-uri misses ongoing injection attempts.
Why this feature exists
CSP spec (W3C) lets sites declare allowlists replacing permissive default-src * behavior. script-src 'unsafe-inline' negates most XSS benefit — staff policies use nonces or hashes instead.
- frame-ancestors: Replaces X-Frame-Options — clickjacking defense.
- upgrade-insecure-requests: Auto-upgrade HTTP subresources.
- Trusted Types: require-trusted-types-for 'script' pairs with CSP in Chromium.
Browser internals
CSP enforcement occurs before script execution — parser hits script tag, checks nonce/hash against policy, blocks inline without match, reports violation to report-to endpoint. Meta CSP cannot set report-uri in all browsers — header preferred.
- Nonce: Cryptographic per-response random on script tag and header must match.
- Hash: sha256 of inline script content — static inline only.
Rendering workflow
CSP rollout workflow: Deploy Report-Only policy → collect violations 2 weeks → fix inline scripts / whitelist CDNs → enforce with nonce middleware at edge → monitor reports → tighten default-src.
- SSR nonce: Generate per request; inject into script tags and CSP header in same render pass.
- Third-party: Inventory script-src hosts — minimize * wildcards.
Feature deep dive
Strict CSP example for HTML app with nonce:
- script-src 'self' 'nonce-RANDOM': Blocks inline without nonce and external unknown hosts.
- object-src 'none': Blocks Flash/plugin vectors.
- base-uri 'self': Prevents base tag hijack.
- frame-ancestors 'none': Anti-clickjacking.
<!-- HTTP header (preferred) -->Content-Security-Policy: default-src 'self';script-src 'self' 'nonce-a1b2c3';style-src 'self' 'nonce-a1b2c3';img-src 'self' https: data:;frame-ancestors 'none';report-uri /csp-report<!-- HTML --><script nonce="a1b2c3" src="/app.js"></script>
Accessibility analysis
CSP blocking inline a11y fixes — move focus management to external nonce script. style-src unsafe-inline sometimes requested for CSS frameworks — prefer nonce on critical inline styles or external stylesheet.
SEO impact
CSP doesn't block Googlebot crawl directly — but blocked analytics doesn't affect indexation. Ensure JSON-LD script uses nonce or is static hash-whitelisted.
Security considerations
OWASP CSP Cheat Sheet — avoid unsafe-inline and unsafe-eval in production. Use strict-dynamic cautiously for script loaders. Monitor violations for injection reconnaissance.
- Bypass: JSONP endpoints, angularjs sandbox escapes historical — keep libraries patched.
Performance impact
Per-request nonces prevent full-page CDN cache of HTML — edge must generate nonce at origin or use hash for static inline chunks only on static pages.
Real production example
Next.js middleware CSP nonce pattern:
- Report-Only: Content-Security-Policy-Report-Only during migration.
export function middleware(request) {const nonce = crypto.randomUUID();const csp = `script-src 'self' 'nonce-${nonce}'; frame-ancestors 'none'`;const res = NextResponse.next();res.headers.set("Content-Security-Policy", csp);res.headers.set("x-nonce", nonce);return res;}
Enterprise usage
Central security team publishes CSP baseline template per app type — marketing sites looser with tag manager host allowlist; admin apps strictest.
Common production failures
unsafe-inline left in policy for one legacy widget — XSS still exploitable. Wrong nonce on cached HTML page — all scripts blocked, checkout down. frame-ancestors omitted — clickjacking on embeddable page.
Architecture review questions
- CSP delivered via HTTP header on all HTML responses?
- script-src excludes unsafe-inline in production?
- Nonce rotation per request on SSR pages?
- frame-ancestors set for clickjacking protection?
- Violation reporting monitored with alerting?
Hands-on project
Project: Add Report-Only CSP to staging; fix violations; promote to enforcing policy with nonces on app scripts.
Interview questions
CSP nonce vs hash — when use each?(Advanced)
Nonce: dynamic SSR pages with inline/script per response — must regenerate each request. Hash: static inline script content unchanged — cacheable HTML. External scripts use host allowlist or strict-dynamic with caution.
Follow-up: Why unsafe-inline defeats CSP?
Roll out strict CSP without breaking production analytics.(Advanced)
Report-Only first; inventory violations; add tag manager domains to script-src; move inline pixels to nonce scripts or server-side tagging; coordinate marketing freeze during enforce window; keep rollback header template.
Follow-up: Meta CSP limitations?
frame-ancestors vs X-Frame-Options?(Intermediate)
frame-ancestors in CSP modern standard — supports allowlist of embedders. X-Frame-Options DENY/SAMEORIGIN older — use CSP frame-ancestors for finer control; send both during transition for legacy IE if needed.
Follow-up: Allow embedding in partner iframe securely?
Try it yourself
Edit the HTML, CSS, or JS panels — the preview updates as you type.
Try it yourself
Summary
Content Security Policy restricts HTML-loaded script and resource origins — OWASP-aligned staff deployments use nonce-based strict policies, frame-ancestors anti-clickjacking, and violation monitoring to contain XSS blast radius.