Content Security Policy Headers Setup Guide for 2026

April 3, 2026 · Security, Auth, Web Security

Content Security Policy (CSP) is still the single most effective browser-side control for preventing cross-site scripting (XSS) and limiting the blast radius of injected content. In 2026, browsers enforce CSP consistently, tooling is better, and most frameworks now expose hooks for nonces and hashes. The challenge is not what CSP is, but how to roll it out without breaking your app.

This guide walks through a practical, production-safe CSP setup: from a report-only rollout to a locked-down policy with nonces and hashes. You’ll see ready-to-use header examples for Nginx, Apache, Node.js, and common CDNs, plus debugging and testing workflows that developers actually use.

What CSP does and why it matters in 2026

CSP tells the browser what sources are allowed for scripts, styles, images, fonts, frames, and network calls. If content violates the policy, the browser blocks it (or reports it in report-only mode). That means:

CSP complements server-side validation and encoding. It doesn’t replace sanitization or output escaping—but it is a strong last line of defense that browsers enforce regardless of your code path.

Start with report-only mode (safe rollout)

Report-only lets you collect violations without breaking production traffic. In 2026, a safe rollout is:

Example report-only header:

Content-Security-Policy-Report-Only: default-src 'self'; script-src 'self' 'unsafe-inline' https://cdn.example.com; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self' https://api.example.com; report-to csp-endpoint;

Note the temporary 'unsafe-inline'—that is typical in report-only while you migrate to nonces or hashes.

Design a baseline policy (secure defaults)

A baseline CSP for a modern web app should include at least these directives:

Example enforcement CSP:

Content-Security-Policy: default-src 'self'; script-src 'self' 'nonce-{{NONCE}}' https://cdn.example.com; style-src 'self' 'nonce-{{NONCE}}'; img-src 'self' data: https://images.example.com; connect-src 'self' https://api.example.com https://events.example.com; font-src 'self' https://fonts.example.com; object-src 'none'; base-uri 'self'; frame-ancestors 'none'; upgrade-insecure-requests; block-all-mixed-content;

upgrade-insecure-requests and block-all-mixed-content are still valuable when you’re forcing HTTPS across legacy assets.

Nonce vs hash: choosing the right strategy

Nonces are random per-response tokens added to script and style tags. They are ideal when you can generate HTML server-side.

Hashes are SHA-256/384/512 of the inline script or style content. They work well for static inline snippets (e.g., a tiny bootloader).

Rules of thumb:

Nonce example (Node.js / Express)

import crypto from 'node:crypto';

app.use((req, res, next) => {
  const nonce = crypto.randomBytes(16).toString('base64');
  res.locals.nonce = nonce;
  res.setHeader(
    'Content-Security-Policy',
    `default-src 'self'; script-src 'self' 'nonce-${nonce}'; style-src 'self' 'nonce-${nonce}'; object-src 'none'; base-uri 'self';`
  );
  next();
});

Then in your template:

<script nonce="{{nonce}}">
  window.__BOOTSTRAP__ = {...};
</script>

Hash example (static HTML)

Content-Security-Policy: default-src 'self'; script-src 'self' 'sha256-2fNnP4v5mH6Z9d0Qxq...='; style-src 'self'; object-src 'none';

Compute the hash with a one-liner:

printf "console.log('hi');" | openssl dgst -sha256 -binary | openssl base64

Real-world CSP headers for popular stacks

Nginx

add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'nonce-$request_id'; style-src 'self' 'nonce-$request_id'; img-src 'self' data:; connect-src 'self' https://api.example.com; object-src 'none'; base-uri 'self'; frame-ancestors 'none'" always;

Note: Nginx cannot generate true random nonces per response without additional modules. Use an app-layer nonce or a nonce from your reverse proxy if supported.

Apache

Header always set Content-Security-Policy "default-src 'self'; script-src 'self' 'nonce-%{UNIQUE_ID}e'; style-src 'self' 'nonce-%{UNIQUE_ID}e'; object-src 'none'; base-uri 'self'"

Node.js (Helmet)

import helmet from 'helmet';

app.use((req, res, next) => {
  res.locals.nonce = crypto.randomBytes(16).toString('base64');
  next();
});

app.use(
  helmet.contentSecurityPolicy({
    directives: {
      defaultSrc: ["'self'"],
      scriptSrc: ["'self'", (req, res) => `'nonce-${res.locals.nonce}'`],
      styleSrc: ["'self'", (req, res) => `'nonce-${res.locals.nonce}'`],
      imgSrc: ["'self'", 'data:'],
      objectSrc: ["'none'"],
      baseUri: ["'self'"],
      frameAncestors: ["'none'"]
    }
  })
);

Reporting: collect violations and fix fast

In 2026, the recommended way is the Reporting API using report-to. You can still use the legacy report-uri for compatibility.

Report-To: {"group":"csp-endpoint","max_age":10886400,"endpoints":[{"url":"https://csp.example.com/report"}]}
Content-Security-Policy-Report-Only: default-src 'self'; script-src 'self' 'unsafe-inline'; report-to csp-endpoint;

Many teams store reports as JSON and analyze them. If you need to quickly inspect the payload, paste the JSON into the DevToolKit JSON Formatter: ../tools/json-formatter.html

Common CSP breakages (and how to solve them)

If you need to quickly test a regex for matching allowed subdomains before updating CSP, use the DevToolKit Regex Tester: ../tools/regex-tester.html

Recommended CSP for SPAs

Single-page apps typically load static bundles and make API calls. A hardened CSP example:

Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.example.com; style-src 'self' 'nonce-{{NONCE}}'; img-src 'self' data: https://images.example.com; connect-src 'self' https://api.example.com https://analytics.example.com; font-src 'self' https://fonts.example.com; object-src 'none'; base-uri 'self'; frame-ancestors 'none';

Most modern SPA bundles don’t need inline scripts at all. That lets you skip nonce for scripts and only use it for inline styles if necessary.

Testing workflow that developers actually use

When you need to encode a report URL into a JSON config or query string, use DevToolKit’s URL Encoder/Decoder: ../tools/url-encoder.html

Should you use CSP nonces or CSP hashes?

In 2026, use nonces for server-rendered apps and hashes for static HTML. If you are shipping a pure SPA with external bundles and no inline script, you can skip both and still be secure. The only time you should consider 'unsafe-inline' in enforcement mode is when you have no control over inline scripts and can’t add nonces or hashes—which is rare and risky.

Checklist: minimum CSP for production

Final thoughts

A CSP rollout is a security project and a dev-experience project. The safest way is to start in report-only mode, resolve violations, then enforce. In 2026, a well-designed CSP is table stakes for any app that handles auth, payments, or user-generated content. If you implement nonces or hashes and keep your allowlist tight, you’ll eliminate a massive class of XSS attacks with minimal ongoing maintenance.

FAQ

Do I need CSP if I already sanitize user input?

Yes, you still need CSP because sanitization can fail and CSP blocks injected scripts even if a bug slips through.

Is report-only CSP safe for production?

Yes, report-only CSP is safe in production because it does not block content and only generates violation reports.

Should I allow 'unsafe-inline' in 2026?

No, you should not allow 'unsafe-inline' in enforcement mode because it defeats CSP’s primary defense against XSS.

Are CSP nonces compatible with CDNs?

Yes, CSP nonces work with CDNs as long as the nonce is generated per response and injected into the HTML before it is cached or personalized.

What’s the fastest way to debug CSP violations?

The fastest way is to enable report-only mode and inspect aggregated reports by directive and blocked URI, then tighten the policy iteratively.

Recommended Tools & Resources

Level up your workflow with these developer tools:

Auth0 → Cloudflare Zero Trust → Web Application Security by Andrew Hoffman →

Dev Tools Digest

Get weekly developer tools, tips, and tutorials. Join our developer newsletter.