Articles

What Is a Content Security Policy (CSP)?

A Content Security Policy is an HTTP header that restricts what scripts and resources a page can load, blocking most XSS attacks by default.

Chisato Chisato · · 4 min read
A red padlock icon overlaid on a keyboard

A Content Security Policy (CSP) is an HTTP response header that tells the browser exactly which sources of scripts, styles, images, and other resources a page is allowed to load. Its main purpose is blocking cross-site scripting (XSS): even if an attacker manages to inject a <script> tag into your page, a properly configured CSP stops the browser from executing it unless it comes from an explicitly allowed source.

It’s a defense-in-depth layer, not a replacement for sanitizing input and encoding output correctly — but it’s one of the highest-leverage single headers you can add to a site.

What CSP protects against

The classic XSS attack works by getting attacker-controlled markup into a page — through an unescaped form field, a URL parameter reflected into HTML, or a stored comment — so that a <script> tag or an inline event handler ends up executing in a victim’s browser with full access to that page’s cookies and DOM.

CSP addresses this at the browser level: even if malicious markup makes it into the page, the browser refuses to execute any script that doesn’t match the policy. An attacker who injects <script src="https://evil.example/steal.js"> gets nothing if your policy only allows scripts from your own origin. This makes CSP effective against the class of bug, not just a specific instance of it — it’s a safety net for the XSS vulnerabilities you haven’t found yet.

How a CSP is delivered

A CSP is usually sent as a Content-Security-Policy HTTP response header, set by your server or CDN on every response. It can also be delivered as a <meta http-equiv="Content-Security-Policy"> tag in the page’s <head>, though the header form is more reliable — a meta tag can’t cover certain directives and only applies after the browser has already started parsing the page.

A minimal policy looks like this:

Content-Security-Policy: default-src 'self'; script-src 'self'; object-src 'none'

This says: by default, only load resources from the page’s own origin; only run scripts from the page’s own origin; and never load plugins like Flash or Java applets at all.

Key directives

CSP is built from directives, each controlling a specific resource type:

  • default-src — the fallback source list used by any directive that isn’t specified explicitly.
  • script-src — where JavaScript can be loaded from, and whether inline <script> blocks or eval() are allowed at all.
  • style-src — where CSS can be loaded from.
  • img-src, connect-src, font-src — analogous restrictions for images, fetch/XHR/WebSocket targets, and fonts.
  • object-src — controls plugins; setting this to 'none' is standard practice.
  • frame-ancestors — restricts which sites are allowed to embed your page in an <iframe>, functioning as a modern, more flexible replacement for the older X-Frame-Options header.

Each directive takes a source list — 'self' for the page’s own origin, specific domains, 'none' to block entirely, or special keywords covered below for inline content.

Nonces and hashes for inline scripts

A strict CSP that only allows script-src 'self' will block inline <script> blocks and inline event handlers (onclick="...") entirely, which breaks a lot of real-world pages that weren’t built with CSP in mind. Two mechanisms let you allow specific inline scripts without opening the door to all of them:

  • Nonces — the server generates a random, single-use token per response, includes it in the CSP header (script-src 'nonce-abc123'), and adds the same value to the nonce attribute of the specific inline <script> tags that should be allowed to run. An attacker injecting their own script tag has no way to know the nonce for that response.
  • Hashes — the CSP header includes the SHA hash of a specific inline script’s exact contents (script-src 'sha256-...'); only a script whose content matches that hash is allowed to execute.

Both approaches let you keep specific, known inline scripts working while still blocking anything an attacker injects, since injected content won’t have a valid nonce or match a pre-computed hash.

Report-only mode

Rolling out a strict CSP on an existing site risks breaking things you didn’t anticipate — a third-party widget, an analytics snippet, a forgotten inline script. The Content-Security-Policy-Report-Only header lets you deploy a policy that logs violations (via the report-uri or report-to directive) without actually blocking anything, so you can see what would have broken before switching to enforcement.

CSP and CORS are not the same thing

It’s easy to conflate CSP with CORS, since both are browser security mechanisms controlled by HTTP headers, but they solve different problems. CORS controls whether another site’s JavaScript is allowed to read a response from your API. CSP controls what your own page is allowed to load and execute, regardless of who’s making the request. A site can have a strict CSP and permissive CORS, or vice versa — they’re independent controls.

CSP also complements other browser-security headers and practices covered elsewhere on the site, including defenses against CSRF and the broader push toward zero trust security models that assume any single layer can fail.

The takeaway

A Content Security Policy is a header-level allowlist for what a page can load and execute, and it’s one of the most effective single mitigations against XSS available — it stops injected scripts from running even when input sanitization fails somewhere. Start with a strict default-src 'self' baseline, use nonces or hashes for the inline scripts you actually need, and roll it out in report-only mode first so you can see what breaks before you enforce it.

Chisato Chisato · · 3 min read

What Is XSS? Cross-Site Scripting Explained

Cross-site scripting (XSS) injects malicious scripts into pages other users view. How stored, reflected, and DOM-based XSS work, and how to prevent them.

#Security #Web Development #JavaScript
Takina Takina · · 4 min read

What Is CORS? Cross-Origin Requests, Explained

CORS lets a server opt in to cross-origin browser requests, relaxing the same-origin policy in a controlled way. Why it exists and how to fix CORS errors.

#Web Development #Security #JavaScript
Takina Takina · · 4 min read

TypeScript Abstract Classes, Explained

Abstract classes in TypeScript define shared implementation plus methods subclasses must fill in. How they differ from interfaces and when to reach for them.

#TypeScript #JavaScript #Web Development