Content Security Policy
content security policy content security policy for css controls which stylesheets load, whether inline styles execute, and from which origins
Introduction
Content Security Policy for CSS controls which stylesheets load, whether inline styles execute, and from which origins url() may fetch — closing XSS gaps when HTML sanitization fails. Staff engineers align style-src with nonce/hash strategies and audit third-party widget CSS.
Business problem
Injected style tags and inline style attributes bypass script-src-only CSP — attackers exfil data or restyle login forms. style-src 'unsafe-inline' negates much of CSP value for CSS-heavy SPAs.
- Compliance: PCI and SOC2 expect CSP on payment pages.
- Third-party: Analytics widgets demand inline styles — policy tension.
Why this feature exists
CSP Level 3 style-src governs <link rel=stylesheet>, <style> blocks, and style="" attributes. Nonce and hash allowlist specific inline; 'unsafe-inline' ignored when nonce present in modern browsers.
- style-src-elem vs style-src-attr: Split control of elements vs attributes (CSP3).
- report-only: Rollout without breaking prod.
Browser rendering perspective
Browser enforces CSP before applying CSS — blocked stylesheet logged to console and report-uri. Inline critical CSS needs nonce on style tag matching header.
- Violation reports: JSON POST to report-uri/report-to endpoint.
Internal browser workflow
CSP rollout: Inventory all CSS sources (bundles, CDN, inline critical, third-party) → report-only 2 weeks → tighten style-src → add nonces to SSR inline styles → block style attributes or use style-src-attr 'none'.
Feature deep dive
CSP headers for CSS:
Content-Security-Policy:default-src 'self';style-src 'self' 'nonce-{RANDOM}';style-src-attr 'none';report-uri /csp-report;<!-- SSR --><style nonce="{RANDOM}">/* critical CSS */</style><link rel="stylesheet" href="/app.css">
Syntax
Meta tag limited — prefer HTTP header:
<meta http-equiv="Content-Security-Policy"content="style-src 'self' 'nonce-abc123'">
Examples
Next.js middleware nonce: Generate per-request nonce; pass to styled-components or emotion if configured; inline style attributes blocked via style-src-attr 'none' forcing classes.
Real-world use
GitHub, Stripe Checkout, and Google publish strict CSP. report-uri monitoring teams detect injection attempts. Hash-based CSP for static inline critical CSS on marketing sites.
Real production example
Edge nonce injection at CDN for HTML document; static CSS from 'self'; block user style=; weekly CSP report review dashboard.
- Hash: sha256-base64 for immutable inline critical CSS chunk if nonce impractical on static CDN.
Enterprise use case
CSP central team maintains policy templates per app tier — marketing relaxed vs checkout strict.
Accessibility considerations
style-src-attr 'none' blocks inline style on elements — ensure all styling via classes; AT not affected if HTML semantics intact.
Performance considerations
Per-request nonces prevent HTML caching at CDN — edge nonce generation or hash for static critical CSS tradeoff.
SEO considerations
CSP violations don't directly affect SEO — but blocked CSS hurts render — monitor accidental over-blocking.
Scalability considerations
Micro-frontends merge CSP at shell — union of style-src origins or single bundle host.
Common production issues
Incidents: Deployed style-src 'self' without new CDN domain — site unstyled 2 hours. Third-party chat widget broke — emergency hash allowlist.
Debugging guide
DevTools Console CSP violation messages name blocked URI. report-only mode collects without user impact.
Best practices
- Deploy style-src via HTTP header not meta when possible.
- Use nonces for inline critical CSS; avoid unsafe-inline.
- Set style-src-attr 'none' if product can ban inline styles.
- Start report-only; monitor before enforce.
- Document third-party CSS origins in inventory.
Anti-patterns
- style-src * or unsafe-inline on checkout.
- Ignoring CSP reports after launch.
- Same nonce reused across users/sessions predictably.
Trade-offs
- Benefit: Blocks injected style XSS when encoding fails.
- Cost: Nonce hurts HTML cache; third-party friction.
Architecture review questions
- style-src excludes unsafe-inline on sensitive pages?
- Inline critical CSS uses nonce or hash?
- CSP reports monitored?
Interview questions
How do you CSP-protect CSS in SSR app?(Advanced)
Per-request nonce in header and inline style tags; style-src 'self' nonce-{n}; style-src-attr 'none'; external bundles from self or SRI CDN; report-only first; hash fallback for static marketing pages.
Follow-up: styled-components with CSP?
Hands-on exercise
Exercise: Add CSP report-only with style-src 'self'; fix violations; switch to enforce; verify injected style blocked.
Staff engineer notes
- CSP is layered defense — encoding still primary.
Common pitfalls
- Forgetting CDN domain in style-src after migration.
Try it yourself
Edit the CSS panel — the preview updates live. Use DevTools Performance and Accessibility panels to validate.
Try it yourself
Summary
Content Security Policy for CSS uses style-src, style-src-attr, nonces, and hashes to block injected stylesheets and inline style XSS — rolled out via report-only monitoring with third-party origin inventory.
Key takeaways
- style-src and style-src-attr control CSS injection surface.
- Use nonces/hashes instead of unsafe-inline.
- Roll out report-only and monitor violations.