{"post":{"slug":"content-security-policy-2026","title":"Content Security Policy in 2026: block XSS before it reaches your users","excerpt":"Content Security Policy is the most underused browser security control available. Here is how to write a CSP that actually stops attacks in 2026.","category":"code","date":"March 27, 2026","author":"ByteLedger Team","content":"<span class=\"dropcap\">C</span>ontent Security Policy is a response header that instructs browsers to enforce a strict allowlist of resources your page is permitted to load. It is one of the most powerful XSS mitigations available — and one of the most commonly misconfigured, rendering it useless in production. Getting CSP right in 2026 means understanding nonces, `strict-dynamic`, and why domain allowlists are almost always wrong.\n\n## What changed in 2026\n\n- **`strict-dynamic` is now the recommended approach.** Instead of maintaining an ever-growing domain allowlist, you issue a per-request nonce and trust only scripts that carry it. `strict-dynamic` then propagates trust to dynamically loaded scripts.\n- **Browser support for CSP Level 3 is universal.** All modern browsers support nonces, `strict-dynamic`, and `report-to`. IE is gone; no compatibility excuses remain.\n- **Trusted Types graduated to a stable standard.** Trusted Types enforce that dangerous DOM APIs (like `innerHTML`) only accept pre-sanitized values, complementing CSP.\n- **Reporting APIs matured.** The `Reporting-Endpoints` header and `report-to` directive replaced the older `report-uri`, giving richer violation reports with batching.\n\n## CSP directives that matter most\n\n| Directive | Controls | Recommended value |\n| --- | --- | --- |\n| `default-src` | Fallback for unlisted directives | `'self'` |\n| `script-src` | JavaScript sources | `'nonce-{n}' 'strict-dynamic'` |\n| `style-src` | CSS sources | `'self' 'nonce-{n}'` |\n| `img-src` | Image sources | `'self' data: https:` |\n| `connect-src` | XHR/fetch/WebSocket | `'self' https://api.myapp.com` |\n| `frame-ancestors` | Who can iframe your page | `'none'` (prevent clickjacking) |\n| `base-uri` | Restricts `<base>` element | `'self'` |\n| `object-src` | Plugins (`<object>`, `<embed>`) | `'none'` |\n| `upgrade-insecure-requests` | Force HTTPS for subresources | Include always |\n\n## A nonce-based strict policy\n\n```python\n# Flask example — generate a fresh nonce per request\nimport secrets\nfrom flask import Flask, render_template_string, g\n\napp = Flask(__name__)\n\n@app.before_request\ndef set_nonce():\n    g.csp_nonce = secrets.token_urlsafe(16)\n\n@app.after_request\ndef add_csp_header(response):\n    nonce = g.csp_nonce\n    csp = (\n        f\"default-src 'self'; \"\n        f\"script-src 'nonce-{nonce}' 'strict-dynamic' https:; \"\n        f\"style-src 'self' 'nonce-{nonce}'; \"\n        f\"img-src 'self' data: https:; \"\n        f\"connect-src 'self' https://api.myapp.com; \"\n        f\"object-src 'none'; \"\n        f\"base-uri 'self'; \"\n        f\"frame-ancestors 'none'; \"\n        f\"upgrade-insecure-requests; \"\n        f\"report-to csp-endpoint\"\n    )\n    response.headers['Content-Security-Policy'] = csp\n    response.headers['Reporting-Endpoints'] = (\n        'csp-endpoint=\"https://myapp.com/csp-report\"'\n    )\n    return response\n```\n\n```html\n<!-- In your template, every inline script gets the nonce -->\n<script nonce=\"{{ g.csp_nonce }}\">\n  // This script is trusted\n  initApp();\n</script>\n```\n\nEvery request gets a new random nonce. An attacker who injects `<script>` tags without the nonce gets blocked by the browser.\n\n## How to start\n\n1. **Deploy in `Content-Security-Policy-Report-Only` mode first.** Add the header with your target policy and a report endpoint. Collect violations for at least two weeks before enforcing.\n2. **Eliminate `unsafe-inline` from scripts.** Replace inline `<script>` tags and `onclick` attributes with external scripts or event listeners. Add nonces to any remaining inline scripts.\n3. **Replace `unsafe-eval`.** Audit your code for `eval()`, `new Function()`, and libraries that use them (some template engines, older jQuery plugins). Replace or remove them.\n4. **Switch domain allowlists to nonces.** A policy with `script-src https://cdn.example.com` trusts everything on that CDN — including any script an attacker could inject there. Nonces are per-request and unforgeable.\n5. **Enable enforcement and monitor reports.** Violations after enforcement indicate something was missed — fix the source or adjust the policy.\n\n## Common mistakes\n\n**Using `'unsafe-inline'`.** This permits all inline scripts, which is precisely what XSS attacks inject. A policy with `unsafe-inline` provides essentially no XSS protection.\n\n**Using `'unsafe-eval'`.** Permits `eval()`, which allows turning arbitrary strings into executable code. Many classic XSS payloads rely on eval. Eliminate eval from your codebase.\n\n**Domain allowlists instead of nonces.** `script-src https://cdnjs.cloudflare.com` trusts every file on that CDN — an enormous surface. If one of those files has a vulnerable version or an attacker can host their payload there, your CSP is bypassed.\n\n**Forgetting `object-src 'none'`.** Flash and plugin content (even mostly dead) can bypass script restrictions. Always explicitly deny it.\n\n**Not testing in report-only mode.** Enforcing a first-draft CSP on a complex app will break your analytics, chat widgets, and payment forms. Report-only mode lets you find those before users do.\n\n## What to skip\n\n- **CSP alone as your XSS defense.** Input sanitization and output encoding are the primary defense; CSP limits the blast radius when they fail. Never skip sanitization because \"we have CSP.\"\n- **Hand-writing CSP for complex SPAs** with server-side rendering and dozens of third-party scripts without tooling. Use a CSP generator or a middleware library that handles nonce injection for your framework.\n- **Ignoring the `frame-ancestors` directive.** Clickjacking is separate from XSS; `frame-ancestors 'none'` prevents your app from being embedded as an iframe and used in a click-jacking attack.\n\n## FAQ\n\n**Will CSP break Google Analytics or other third-party scripts?**\nYes, if configured without those sources. Add third-party scripts via nonce (if you control the HTML injection) or via a hash. Avoid adding entire domains to `script-src` if possible — prefer nonces.\n\n**What is Trusted Types and do I need it?**\nTrusted Types is a browser API that forces dangerous DOM sinks (like `innerHTML`) to accept only sanitized `TrustedHTML` objects rather than raw strings. It pairs well with CSP; a strict CSP + Trusted Types together make DOM-based XSS extremely difficult. It's enabled via the `require-trusted-types-for 'script'` directive.\n\n**How do I handle inline styles for CSS-in-JS?**\nCSS-in-JS libraries (styled-components, Emotion) can inject inline `<style>` blocks. Options: generate a hash for each injected style block (some libraries support this), use style nonces, or configure the library to extract static styles at build time.\n\n**What CSP grade should I aim for?**\nThe [Google CSP Evaluator](https://csp-evaluator.withgoogle.com/) (still the best free tool in 2026) grades policies. Aim for no \"high severity\" findings. A nonce-based policy with `strict-dynamic` typically scores well; domain allowlist policies do not.\n\n## Where to go next\n\n[JWT best practices in 2026](/blog/jwt-best-practices-2026), [OAuth explained in 2026](/blog/oauth-explained-2026), and [Feature flags guide in 2026](/blog/feature-flags-guide-2026).","hue":321,"metaTitle":"Content Security Policy 2026: Block XSS with a Strict CSP","metaDescription":"Write an effective Content Security Policy in 2026 using nonces and strict-dynamic. Avoid the common CSP mistakes that leave your site wide open.","keywords":["content security policy 2026","csp header","csp nonce","strict-dynamic csp","xss prevention","csp report-only","content security policy best practices","csp directive","web security headers","csp implementation 2026"],"tldr":["**CSP is a browser-enforced allowlist** — it tells browsers which sources are allowed to load scripts, styles, and other resources on your page.","**Use nonces or strict-dynamic instead of domain allowlists** — domain lists are always too broad and bypass CSP's protection in practice.","**Start in report-only mode** — collect violations for two weeks before enforcing, or you will break your own site.","**CSP is a second line of defense, not a substitute** — sanitize inputs and encode outputs first; CSP limits the damage when that fails.","**Skip:** using `'unsafe-inline'` and `'unsafe-eval'` in a policy you call \"security\" — those directives effectively disable XSS protection."],"readTime":"4 min read","wordCount":975},"related":[{"slug":"llm-security-best-practices-2026","title":"LLM Security Best Practices in 2026: A Practical Guide","excerpt":"Securing an LLM-integrated application means assuming the model cannot tell trusted instructions from untrusted data. Here are the 2026 practices that actually reduce risk, from tool scoping to RAG access control.","category":"code","date":"August 6, 2026","author":"ByteLedger Team","readTime":"4 min read","hue":312},{"slug":"cdns-explained-2026","title":"CDNs explained in 2026: how content delivery networks actually work","excerpt":"CDNs have evolved far beyond static file caching — they now run code at the edge and shape latency for every layer of your stack. Here is what matters in 2026.","category":"code","date":"February 20, 2026","author":"ByteLedger Team","readTime":"5 min read","hue":309},{"slug":"api-versioning-strategies-2026","title":"API Versioning Strategies in 2026: Picking One That Lasts","excerpt":"Every public API eventually has to change in a way that breaks existing clients. Here is how the main versioning strategies compare and how to pick one before you need it.","category":"code","date":"July 5, 2026","author":"ByteLedger Team","readTime":"4 min read","hue":240}],"sameCategory":[{"slug":"bazel-build-explained-2026","title":"Bazel Explained: What Hermetic Builds Actually Cost","excerpt":"Bazel delivers reproducible, precisely cached builds and demands that you describe everything. Here is what you get, what it costs, and who should say no.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":145},{"slug":"graphql-federation-2026","title":"GraphQL Federation: One Graph, Many Teams","excerpt":"Federation composes independently-owned subgraphs into a single API. Here is what it solves, the operational cost it adds, and when a simpler gateway is enough.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":300},{"slug":"hermetic-builds-explained-2026","title":"Hermetic Builds Explained: Same Inputs, Same Output","excerpt":"A hermetic build depends only on declared inputs. Here is what that property gives you, the common leaks that break it, and how to move toward it incrementally.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":130},{"slug":"monorepo-tooling-compared-2026","title":"Monorepo Tooling Compared: What You Actually Need","excerpt":"Monorepo tools solve task orchestration and caching, not repository layout. Here is what each tier provides and how to tell when you have outgrown the simple option.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":150}],"newer":{"slug":"confirmation-bias-explained-2026","title":"Confirmation bias explained in 2026: why you find what you were already looking for","excerpt":"Confirmation bias makes you collect evidence for beliefs you already hold while discarding everything that challenges them. Here is how to catch and correct it in 2026.","category":"guides","date":"March 27, 2026","author":"ByteLedger Team","readTime":"5 min read","hue":270},"older":{"slug":"cors-deep-dive-explained-2026","title":"CORS deep dive in 2026: why it exists, how it works, how to fix it","excerpt":"CORS errors are some of the most common and most misunderstood issues in web development. This 2026 guide explains the security model, preflight requests, and the correct server-side fix — not the wildcard shortcut.","category":"code","date":"March 27, 2026","author":"ByteLedger Team","readTime":"5 min read","hue":317}}