How do I fix a missing Content-Security-Policy header?

A missing Content-Security-Policy header means the browser receives no instruction about which origins may load scripts, styles, frames, and other resources on your pages. The Content-Security-Policy response header lets site administrators control which resources a user agent is allowed to load, and its primary purpose is to guard against cross-site scripting. Because the policy is delivered as a response header, the fix is to emit it from your CDN, reverse proxy, or application server on the HTML responses your users actually receive.

Why this matters

With no policy, an injected inline script or a compromised third-party bundle runs with the document's full privileges: it can read cookies that are not HttpOnly, rewrite the DOM, and issue authenticated requests as the user. MDN notes that, with a few exceptions, policies mostly involve specifying server origins and script endpoints, so even a modest policy sharply reduces the reach of injected code. CSP also carries the frame-ancestors navigation directive, which specifies the valid parents that may embed a page in a frame, iframe, object, or embed element. That means one header can restrict script sources and frame embedding together, which is why an absent CSP is scored as a High-severity issue rather than a cosmetic one. Deploying the header after an incident is materially harder than shipping it from the first release, because you must first inventory every origin the application depends on.

How to fix it

Send the header from whatever terminates TLS for your site. The value is a semicolon-separated list of policy directives, and each directive is a name followed by a space-separated list of source expressions. The examples below start from default-src 'self', block embedded plugins with object-src 'none', lock the base URL with base-uri 'self', and limit embedding with frame-ancestors 'self'; widen a directive only to origins you actually use. In Nginx the add_header directive takes a name and a value, may be placed in http, server, or location context, and accepts an always parameter that adds the field regardless of response code. Apache's mod_headers module provides Header always set for the same job, and Express exposes res.set(field, value) or the Helmet middleware. Next.js applies headers from next.config.js, Cloudflare Pages reads a _headers file for static assets, and a Cloudflare Worker can attach headers through a Response's init object.

# Nginx (http, server, or location context)
add_header Content-Security-Policy "default-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'self'" always;

# Apache (server config, vhost, directory, or .htaccess)
Header always set Content-Security-Policy "default-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'self'"

// Express
app.use(function (req, res, next) {
  res.set('Content-Security-Policy', "default-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'self'");
  next();
});

// next.config.js
module.exports = {
  async headers() {
    return [
      {
        source: '/:path*',
        headers: [
          { key: 'Content-Security-Policy', value: "default-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'self'" },
        ],
      },
    ]
  },
}

# Cloudflare Pages public/_headers
/*
  Content-Security-Policy: default-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'self'

// Cloudflare Workers
export default {
  async fetch() {
    return new Response('ok', {
      headers: { 'Content-Security-Policy': "default-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'self'" },
    });
  },
};

How to verify the fix

Request a real HTML page and confirm the header appears on the document response. The shell one-liner below sends a HEAD request and filters the output, and the same value is visible in browser DevTools under Network, on the document request, in the Response Headers pane. Check more than one route, because a CDN rule scoped to a path prefix such as /app/* will not protect marketing pages.

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

Automated check

The Auditor fetches your page, inspects the response headers, and raises a Missing Content-Security-Policy header finding at High severity when the header is absent. That tells you whether the control is deployed at all; fixing the finding is not the same as shipping a strong policy, so compare the value you emit against MDN's Content-Security-Policy reference and remove unsafe-inline and unsafe-eval wherever you can.

Scan your app free →

Related issues