How to set the Content-Security-Policy header in every framework
CentralCSP Team ·
Last update:
A Content Security Policy (CSP) only protects users when the browser actually receives it, and the right way to deliver it is a response header. This page collects the shortest way to send that header in the stacks people ask about most, so you can find yours, paste it, and move on to tuning the policy itself.
Two rules apply to every example below. Start in Report-Only (use the
Content-Security-Policy-Report-Only header name) so a policy that is too strict
reports instead of breaking the page while you tune it. And set the header as high
up the stack as you reasonably can, at the reverse proxy or the framework's global
response hook, so you do not have to remember it on every route. For building the
policy value itself, see
how to build a strong CSP.
nginx
add_header Content-Security-Policy "default-src 'self'" always;Put this in the server or location block, using the
add_header directive.
The always flag matters: without it, nginx skips the header on error responses
(4xx and 5xx), which are exactly the pages an attacker might reach.
Apache
Header always set Content-Security-Policy "default-src 'self'"This goes in the virtual host or an .htaccess file, and needs
mod_headers enabled.
As with nginx, always ensures the header is set on error responses too.
Express (Node.js)
app.use((req, res, next) => {
res.setHeader("Content-Security-Policy", "default-src 'self'");
next();
});Registered as the first middleware, this applies the header to every Express response. Most real apps reach for Helmet, which sets CSP and other security headers and supports a per-request nonce, so the same middleware that sets the header also injects the nonce into the template. For the nonce pattern itself, see how to set up a per-request nonce.
Django
# in middleware or a response processor
response["Content-Security-Policy"] = "default-src 'self'"A custom Django middleware
that sets the header on the outgoing response is the simplest approach, and the
django-csp package wraps this with per-view
overrides and nonce support; check its current docs for the exact setting names.
Next.js
Set the header in next.config.js for a static policy, or in the request-time proxy
when you need a per-request nonce (the common case once you remove 'unsafe-inline').
That request-time file is proxy.ts with an exported proxy function, renamed from
middleware.ts in Next.js 16 and later.
Next.js documents the full CSP and nonce flow;
the App Router plus proxy nonce pattern has enough moving parts that it also has
its own post here: CSP nonce in Next.js. For running a tag
manager under that policy, see GTM under a strict CSP in Next.js.
async headers() {
return [{ source: "/(.*)", headers: [
{ key: "Content-Security-Policy", value: "default-src 'self'" }
]}];
}Nuxt
// nuxt.config, route rules or a server middleware
routeRules: { "/**": { headers: { "Content-Security-Policy": "default-src 'self'" } } }Nuxt route rules cover static and
server-rendered responses in one place; confirm the exact routeRules header key
against the current Nuxt docs, and for a per-request nonce use server middleware
instead.
Laravel
$response->headers->set('Content-Security-Policy', "default-src 'self'");Register the middleware in the global HTTP kernel so it runs on every response, then read the nonce from the request in your Blade templates.
Angular
Angular is a client-side framework, so it does not send
response headers itself. The CSP comes from whatever serves the built files: nginx, a
Node server, or your static host. Set it there, using the relevant block above.
Angular's runtime also injects styles, so per its
security guide a strict policy needs a
style-src strategy (a nonce or hashes) rather than 'unsafe-inline'.
Confirm the header is actually sent
A common trap is setting the policy in a <meta> tag and assuming it is equivalent.
It is not: a meta-tag policy cannot use frame-ancestors, sandbox, or reporting,
and it only applies to content parsed after the tag. Verify the real response header
with the security headers scanner, and read the
trade-offs in CSP meta tag vs header.
A scan proves the header is sent on the URL you scanned. Frameworks make that a per-route question: middleware that misses a route, a static export served straight from a CDN, an error page rendered outside the stack. Adding a reporting endpoint closes that gap, because CentralCSP records which of your pages produced violations and which produced nothing at all, and a page that never reports is usually a page the header never reached.
Next steps
- Build the policy with how to build a strong CSP.
- Drop host allowlists with strict-dynamic explained.
- Scan your live policy with the CSP scanner.
Monitor your CSP across every page.