Security Headers

Fix missing or weak Content Security Policy (CSP)

Quick fix guide with step-by-step instructions. Barrion detects this finding in your scans; use this page to remediate it.

What it is

Content-Security-Policy (CSP) is a response header that tells the browser which sources of scripts, styles, images, and other resources are allowed to load. It reduces the impact of XSS and data injection by restricting where content can come from.

Why it matters

Without CSP, a single XSS or injected script can load and run any code. CSP limits the damage by blocking unauthorized scripts and inline code. A well-configured CSP is a strong defense-in-depth measure.

How it is exploited

An attacker finds a reflected XSS in your search box and injects a script tag pointing to attacker.example/payload.js. Without CSP the browser fetches and runs it, so the payload reads document.cookie and posts the session token to the attacker's collector. A correct script-src would refuse the off-origin load and the inline injection.

How to fix it

  1. Start with report-only (optional). Use Content-Security-Policy-Report-Only first to see what would be blocked without enforcing. Fix violations, then switch to enforcing CSP.
  2. Set default-src. default-src defines the fallback for most directives. Use 'self' to allow same-origin only, and add specific sources for scripts, styles, and images as needed.
  3. Restrict script-src. script-src controls where JavaScript can load from. Avoid 'unsafe-inline' and 'unsafe-eval' when possible; use nonces or hashes for inline scripts.
  4. Add frame-ancestors for clickjacking. frame-ancestors limits who can embed your site in an iframe. Use 'none' or 'self' to prevent clickjacking.
  5. Tighten beyond the baseline. style-src 'unsafe-inline' is a frequent finding in scanners; remove it once you have hashed or nonced your inline styles. Replace 'unsafe-inline' with nonces or hashes for any remaining inline styles. This is the most common scanner finding once default-src is in place.
  6. Deploy and verify. Deploy the header, test your site, and run a CSP checker or Barrion security scan to confirm the policy is present and effective.

Examples by platform

Nginx

add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data: https:; object-src 'none'; base-uri 'self'; frame-ancestors 'none'; upgrade-insecure-requests;" always;

Apache

Header set Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data: https:; object-src 'none'; base-uri 'self'; frame-ancestors 'none'; upgrade-insecure-requests;"

Next.js (nonce + strict-dynamic)

// middleware.ts
import { NextResponse } from 'next/server';
import crypto from 'crypto';

export function middleware() {
  const nonce = crypto.randomBytes(16).toString('base64');
  const csp = `default-src 'self'; script-src 'self' 'nonce-${nonce}' 'strict-dynamic'; style-src 'self'; img-src 'self' data: https:; object-src 'none'; base-uri 'self'; frame-ancestors 'none'; upgrade-insecure-requests;`;
  const res = NextResponse.next();
  res.headers.set('Content-Security-Policy', csp);
  res.headers.set('x-nonce', nonce);
  return res;
}

How to verify the fix

Confirm the Content-Security-Policy header is present in HTTPS responses:

curl -sI https://example.com | grep -i content-security-policy

References

Related reading

Frequently asked questions

What is a strict CSP?
A strict CSP relies on nonces or hashes for inline scripts and uses 'strict-dynamic' instead of long host allowlists. It blocks injected scripts even when an allowlisted CDN gets compromised.
Should I use nonces or hashes?
Use a nonce when the inline script is generated per request, since the nonce value changes on every response. Use a hash when the inline script is static and can be hashed at build time.
Why does my inline script break under CSP?
By default CSP blocks inline scripts and inline event handlers like onclick. Either move the code to an external file under script-src, attach handlers in JavaScript, or allow the inline block with a matching nonce or hash.
What is the difference between Content-Security-Policy and Content-Security-Policy-Report-Only?
The enforcing header blocks violations and may break pages if the policy is wrong. The report-only header logs violations to your endpoint without blocking, which is useful for tuning a policy before turning it on.

Check your site for this finding.

Run Barrion's free security headers check to see if this applies to your app, with a full report and remediation steps.