TechToolsCenter

Can't find the tool you're looking for?

Request it and vote on what we build next — it takes 20 seconds.

Request a Tool
TechToolsCenter

All Your Essential Tools. One Center. Free, fast, privacy-first online tools that run entirely in your browser.

Built for speed. Designed for privacy. Made for everyone.

Collections

  • Everyday Essentials
  • Calculator Hub
  • Converter Hub
  • Text Studio
  • Business Toolkit
  • PDF Toolkit
  • Image Studio

Popular tools

  • AI Studio
  • Estimate Maker
  • Purchase Order Maker
  • Delivery Challan Maker
  • Invoice Maker
  • Quotation Generator

Company

  • All tools
  • About
  • Updates
  • Community
  • Analytics
  • Contact
  • Editorial policy
  • Privacy
  • Sitemap

Copyright © 2026 TechToolsCenter. All Rights Reserved.

Curated & Coded by Incinc Media Team

HomeTools
  1. Home
  2. Blog
  3. Developer
  4. What Is a Content Security Policy (CSP), and How Does It Prevent XSS?
Developer September 5, 2026 10 min read

What Is a Content Security Policy (CSP), and How Does It Prevent XSS?

A CSP tells the browser exactly which sources of scripts, styles and images a page is allowed to load — so even if an attacker manages to inject a script tag, the browser simply refuses to run it.

TCTechToolsCenter Team

On this page

  • The problem CSP actually solves
  • How CSP actually works: an allowlist, not a blocklist
  • The core directives
  • A real example policy
  • 'unsafe-inline' and 'unsafe-eval': the two settings that undo most of CSP's protection
  • Nonces and hashes: allowing specific inline scripts safely
  • CSP via HTTP header vs the `<meta>` tag: not fully equivalent
  • Report-only mode: testing a policy without breaking your site
  • Common CSP mistakes
  • A concrete example: debugging a blocked resource
  • Third-party scripts you don't fully control
  • How to check whether a site has a CSP, and what it allows
  • CSP and single-page apps / frameworks with build tooling
  • CSP vs other security headers
  • Getting started without rewriting everything at once

A Content Security Policy (CSP) is an HTTP response header (or, less fully, a `<meta>` tag) that tells the browser exactly which sources of scripts, stylesheets, images, fonts and other resources a page is allowed to load and execute. It's one of the most effective defenses against cross-site scripting (XSS) — even if an attacker successfully injects a `<script>` tag into your page, a properly configured CSP makes the browser simply refuse to execute it, because that script's source isn't on the allowed list.

The problem CSP actually solves

Sanitizing user input and escaping output correctly is the first line of defense against XSS, but it's not foolproof — a missed edge case, a third-party library with its own vulnerability, or a new injection vector nobody anticipated can still let attacker-controlled markup or scripts onto the page. CSP is a second, independent layer: even when an injection does slip through, the browser itself enforces the policy and blocks the injected script from running, because CSP operates at the browser level, not the application level — it doesn't rely on your sanitization logic having been correct.

Sponsored

How CSP actually works: an allowlist, not a blocklist

CSP works on an allowlist model — you declare which sources are allowed for each resource type, and everything not explicitly allowed is blocked by default. This is the opposite of trying to blocklist known-bad patterns (which is exactly the losing game traditional XSS filtering plays) — instead of trying to anticipate every way an attacker might inject a script, CSP simply refuses to execute *any* script that doesn't come from a source you've explicitly trusted, regardless of how it got onto the page.

The core directives

  • default-src — the fallback source list used for any resource type that doesn't have its own more specific directive.
  • script-src — which sources JavaScript can be loaded and executed from. The single most important directive for XSS protection.
  • style-src — which sources CSS can be loaded from.
  • img-src — which sources images can be loaded from.
  • connect-src — which origins `fetch`, `XMLHttpRequest`, and WebSocket connections are allowed to reach.
  • frame-ancestors — which sites are allowed to embed this page in an `<iframe>` — the modern, more flexible replacement for the older `X-Frame-Options` header.
  • object-src — controls `<object>`, `<embed>` and `<applet>` — commonly set to `'none'` since these are rarely needed and are a historical source of plugin-based exploits.
  • base-uri — restricts what values are allowed in a `<base>` tag, closing off a specific attack where an injected `<base>` tag silently redirects all the page's relative URLs.

A real example policy

Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.example.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; connect-src 'self' https://api.example.com; object-src 'none'; base-uri 'self'; frame-ancestors 'none'

Reading this: by default, only load resources from the page's own origin (`'self'`); allow scripts from the same origin plus one trusted CDN; allow inline styles (a common practical compromise, discussed below); allow images from the same origin, `data:` URIs, and any HTTPS source; allow API connections to the same origin plus one specific API host; block plugins entirely; restrict `<base>` tags to the same origin; and refuse to let any site embed this page in an iframe at all.

'unsafe-inline' and 'unsafe-eval': the two settings that undo most of CSP's protection

This is the single most important nuance to understand. `'unsafe-inline'` in `script-src` allows any inline `<script>` tag or inline event handler (`onclick="..."`) to run — but this is exactly the mechanism most XSS attacks rely on to inject and execute a script, since an attacker's injected markup is, by definition, inline. Adding `'unsafe-inline'` to `script-src` because your app has inline scripts and you don't want to refactor them defeats the primary purpose of CSP against XSS — an attacker's injected inline script now runs just as freely as your own. `'unsafe-eval'` similarly allows `eval()`, `Function()` constructors, and similar dynamic code execution, another common vector once popular in older libraries. A policy with both of these in `script-src` still blocks *externally-hosted* malicious scripts, but does essentially nothing against inline injection, which is the more common real-world XSS pattern.

Nonces and hashes: allowing specific inline scripts safely

If you genuinely need some inline scripts (a small bootstrapping snippet, an inline analytics init call) without opening the door to `'unsafe-inline'`, CSP supports two safer mechanisms. A nonce is a random, single-use value generated fresh on every page load, added both to the CSP header (`script-src 'nonce-abc123'`) and as an attribute on the specific `<script nonce="abc123">` tag you want to allow — since the nonce changes every request, an attacker can't predict it and inject a script that matches. A hash (`script-src 'sha256-<hash of the script content>'`) instead allows a specific, known, unchanging inline script by its exact content hash — any injected script with different content simply won't match the hash and gets blocked. Nonces suit dynamically-rendered pages; hashes suit static, unchanging inline scripts.

CSP via HTTP header vs the `<meta>` tag: not fully equivalent

CSP can be set either as a real HTTP response header or as a `<meta http-equiv="Content-Security-Policy">` tag in the page's `<head>`. The header is the more complete, more reliable option — critically, `frame-ancestors` (and a couple of other directives) simply don't work when set via the meta tag, only via the real header, because by the time the browser has parsed far enough into the HTML to read a meta tag, framing decisions may already have been made. If you don't control server headers (a purely static host with no configurable headers, for instance) the meta tag is better than nothing, but it isn't a full substitute for a properly configured header.

Report-only mode: testing a policy without breaking your site

Rolling out a strict CSP on a live, already-built site risks breaking things you didn't anticent — a forgotten third-party widget, an analytics script, a font loaded from an unexpected origin. The `Content-Security-Policy-Report-Only` header lets you deploy a policy that only *reports* violations (to a URL you specify via the `report-uri`/`report-to` directive) without actually blocking anything, so you can see exactly what a stricter policy would break before enforcing it. The practical rollout pattern: deploy report-only, watch the violation reports for a realistic period, tighten the policy based on genuinely necessary sources, then switch to the enforcing header once the reports are clean.

Common CSP mistakes

  • **A default-src of `*` (wildcard)** — technically satisfies "we have a CSP" while providing essentially no real protection, since it allows loading from anywhere.
  • Reflexively adding `'unsafe-inline'` to get an existing site's inline scripts working, rather than migrating them to nonces/hashes or external files — this is the single most common way a CSP ends up providing little real XSS protection.
  • Forgetting third-party scripts (analytics, ad networks, chat widgets, payment SDKs) when writing `script-src`/`connect-src` — these often need explicit source entries, and missing one simply breaks that feature rather than failing loudly in an obvious way.
  • Not testing in report-only mode first on an existing site, leading to a surprise outage when a legitimate but unlisted resource gets blocked in production.
  • Setting CSP only via meta tag when `frame-ancestors` or other header-only directives were actually needed for the intended protection (like preventing clickjacking).

A concrete example: debugging a blocked resource

Say a page sets `script-src 'self' https://cdn.example.com` and, after a deploy, a chat widget stops working. Opening DevTools' Console shows a message like "Refused to load the script 'https://widget.chatvendor.com/embed.js' because it violates the following Content Security Policy directive: script-src 'self' https://cdn.example.com" — the browser is telling you, precisely, which resource it blocked and which directive blocked it. The fix is equally precise: add `https://widget.chatvendor.com` to `script-src` (and, separately, whatever origin the widget itself needs for `connect-src` if it makes its own network calls) — not loosen the whole policy. This is the normal, expected workflow for maintaining a CSP over time as a site adds new third-party integrations, not a sign that something's broken.

Third-party scripts you don't fully control

Analytics platforms, ad networks, payment SDKs, and embedded widgets are the most common source of CSP friction, since they're scripts you rely on but don't author yourself — and many of them dynamically load additional sub-resources from origins that aren't obvious until you actually watch what breaks. Some vendors publish the exact CSP entries their script needs; for others, report-only mode combined with genuinely watching real user traffic for a representative period is the only reliable way to build a complete, accurate allowlist rather than guessing and hoping nothing important was missed. This is also why a rushed CSP rollout without a report-only phase tends to either break real functionality or, more often, get walked back to something overly permissive the first time it does — starting cautious and tightening over time works out better than the reverse.

How to check whether a site has a CSP, and what it allows

Open any page's DevTools, go to the Network tab, click the main document request, and check its Response Headers for `Content-Security-Policy` — its full value is right there. The Console tab also directly reports any CSP violations as the page runs (a blocked script, a blocked connection), which is genuinely useful both for auditing someone else's site and for debugging your own policy during rollout.

CSP and single-page apps / frameworks with build tooling

Modern frontend frameworks complicate strict CSP in a specific, common way: many bundlers, by default, inject inline `<script>` or inline `<style>` tags for critical CSS or small runtime bootstrapping code, which a strict `script-src`/`style-src` without `'unsafe-inline'` will block outright. Most major frameworks and build tools now have documented options to either move that inline code into hashed/nonce'd script tags automatically, or to externalize it into separate files entirely — worth checking your specific framework's CSP documentation early in a project rather than discovering the conflict after a strict policy is already in production. Retrofitting CSP support onto a large, already-built app that assumes unrestricted inline scripts is meaningfully more work than planning for it from the start.

CSP vs other security headers

CSP is one of several security-related HTTP headers, each covering a different threat. HSTS (`Strict-Transport-Security`) forces the browser to only ever connect over HTTPS, protecting against downgrade attacks — a different concern from XSS entirely. X-Content-Type-Options: nosniff stops the browser from guessing a resource's MIME type in a way that could execute unintended content. `frame-ancestors` (within CSP) has largely superseded the older, separate `X-Frame-Options` header for clickjacking protection, though some sites still set both for older-browser compatibility. None of these substitute for each other — a genuinely well-secured site layers several of them together, each closing a different gap.

Getting started without rewriting everything at once

A realistic first CSP for an existing, already-live site doesn't need to be maximally strict on day one. A reasonable starting point restricts `object-src` to `'none'` (almost nothing legitimately needs plugins today, so this is close to zero-risk) and sets `base-uri 'self'` and a sensible `frame-ancestors`, while leaving `script-src`/`style-src` closer to what the site currently needs — then tightening those two specifically, in report-only mode, once the rest of the policy is stable. Since `script-src` is where the real XSS protection lives, it's fine to treat it as the thing you iterate toward over weeks rather than something that has to be perfect on the first deploy — a partially-strict CSP that's actually enforced beats a perfect one still sitting in a draft PR.

The short version: CSP is an allowlist that tells the browser exactly which sources are trusted for scripts, styles, images and other resources, blocking anything else — including most injected XSS payloads — regardless of how they got onto the page. Its real strength depends entirely on how strict `script-src` actually is: a policy that includes `'unsafe-inline'` provides only partial protection against the most common real-world XSS pattern. Roll a new policy out in report-only mode first, and prefer nonces or hashes over `'unsafe-inline'` for any inline scripts you genuinely need.

None of this replaces good engineering practice elsewhere — proper output escaping, a well-maintained dependency tree, and careful handling of user-generated content all still matter. CSP is the safety net underneath those practices, catching what slips through, not a substitute for doing them well in the first place.

Tools used in this article

Meta Tags GeneratorGenerate SEO meta, OpenGraph and Twitter Card tags.Robots.txt GeneratorCreate a robots.txt file with rules and sitemap.URL Encoder / DecoderPercent-encode or decode URLs and query parameters.JSON FormatterBeautify, minify and validate JSON with error messages.

Sponsored

Frequently asked questions

No — CSP is a second, independent layer of defense, not a replacement for proper input sanitization and output escaping. It catches injections that slip past your application-level defenses, but shouldn't be the only defense.

TC

TechToolsCenter Team

Product & Tools

The team behind TechToolsCenter — building fast, private, browser-based tools and writing practical guides on how to get the most out of them.

Related articles

Developer 10 min

CORS Explained: Why Your API Requests Get Blocked (and How to Fix It)

"Blocked by CORS policy" is one of the most common errors in web development — and one of the most commonly misunderstood, since the fix almost never lives where the error appears.

TechToolsCenter EditorialRead
Developer 2 min

How to Verify a File's Checksum Before You Trust a Download

A publisher posting a SHA-256 checksum next to a download link is telling you exactly how to check the file wasn't corrupted or tampered with — here's how to actually use it.

TechToolsCenter TeamRead
Developer 2 min

MD5 vs SHA-256: What's the Difference and Which Should You Use?

Both turn data into a fixed-length fingerprint, but MD5 is broken for anything security-sensitive while SHA-256 isn't — here's what that actually means in practice.

TechToolsCenter EditorialRead

On this page

  • The problem CSP actually solves
  • How CSP actually works: an allowlist, not a blocklist
  • The core directives
  • A real example policy
  • 'unsafe-inline' and 'unsafe-eval': the two settings that undo most of CSP's protection
  • Nonces and hashes: allowing specific inline scripts safely
  • CSP via HTTP header vs the `<meta>` tag: not fully equivalent
  • Report-only mode: testing a policy without breaking your site
  • Common CSP mistakes
  • A concrete example: debugging a blocked resource
  • Third-party scripts you don't fully control
  • How to check whether a site has a CSP, and what it allows
  • CSP and single-page apps / frameworks with build tooling
  • CSP vs other security headers
  • Getting started without rewriting everything at once

Sponsored