What is CSP? A guide to browser security
Author: Domeny.ForSale editorial team Published
In brief: CSP, or Content Security Policy, is a browser security mechanism that lets you specify where a page can load resources from and which code it can execute. It helps limit the impact of XSS, the injection of an attacker’s script into a page. A dedicated directive also protects against embedding the site in unwanted frames. The best approach is to send the policy in a HTTP response header and test it without blocking resources first.
How does CSP work, and what does it protect against?
A browser normally loads resources specified in a page’s code: scripts, stylesheets, images and fonts. CSP adds a set of rules to this process. If a resource or the way code is executed does not comply with the policy, the browser blocks it and logs a violation in its developer tools.
The main purpose of CSP is to limit XSS (cross-site scripting) attacks. These attacks cause an attacker’s script to execute in the context of someone else’s website. For example, a vulnerability in a comment form may allow someone to insert JavaScript code instead of plain text. A sufficiently restrictive CSP can prevent that code from running, even if the application mistakenly places it on the page.
CSP also helps prevent clickjacking: embedding a page in a frame and tricking users into clicking its elements beneath a deceptive overlay. The frame-ancestors directive provides this protection. Setting rules for scripts alone does not.
However, CSP does not fix application vulnerabilities. Proper output encoding, safe handling of user content and software updates are still necessary. The policy is an additional layer of protection, not a replacement for these measures.
Where do you set a CSP policy?
The recommended place is a HTTP response header named Content-Security-Policy. The application, web server, reverse proxy or CDN can add it. The header should reach the browser along with the HTML document it applies to.
Some rules can also be delivered through a meta tag with the http-equiv="Content-Security-Policy" attribute. This approach has limitations: the policy takes effect only after the tag is read, and it supports neither report-only mode nor the frame-ancestors directive. If you can configure the HTTP response, use the header.
Also check whether another layer of your infrastructure already adds CSP. Multiple policies are enforced at the same time—an additional header does not automatically replace the previous one. It may therefore impose further restrictions on the site.
Key directives and a sample header
A directive is an individual policy rule. Directives are separated by semicolons, and each directive name is followed by allowed sources or special values.
| Directive | What it controls |
|---|---|
default-src | Default sources for many resource types that do not have their own rule |
script-src | Script sources and the conditions for executing JavaScript |
style-src | Stylesheet sources and rules for using inline styles |
img-src | Image sources |
connect-src | Connections initiated by mechanisms including fetch and WebSocket |
object-src | Embedded objects handled by elements such as object |
base-uri | Addresses allowed in the base element |
frame-ancestors | Pages allowed to embed the document in a frame |
For a simple site that uses only its own files, this could be a starting point:
Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self'; connect-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'none'
The value 'self' means the same origin: a matching scheme, hostname and port. 'none' allows no sources. The example above prohibits embedding the page in frames and blocks, among other things, scripts written directly in HTML and event attributes such as onclick.
This is not a universal configuration. If the site uses an external payment system, analytics or fonts, you need to identify the required sources and add them to the appropriate directives. default-src does not replace every rule—for example, frame-ancestors requires a separate declaration.
How do you deploy CSP without accidentally breaking the site?
The safest approach is to understand the site’s actual dependencies before enabling blocking.
- List resources and integrations. Include not just the home page, but also login, forms, payments and the user dashboard.
- Set the
Content-Security-Policy-Report-Onlyheader. The browser will evaluate the proposed policy and report violations without blocking the affected resources or actions. Collecting reports automatically requires additional configuration of the reporting mechanism and a receiving endpoint. - Test complete user journeys. Check the browser console and network panel. Distinguish necessary resources from unnecessary integrations and requests originating from browser extensions.
- Adjust the rules and code. After testing, switch the verified policy to
Content-Security-Policy, then monitor errors after deployment.
Do not silence warnings by blindly adding *, 'unsafe-inline' or 'unsafe-eval'. These exceptions can significantly weaken protection. For necessary inline scripts, you can use a nonce—an unpredictable value generated for each response—or a hash, a digest of a particular script’s contents. The nonce must match in the header and the script element; a fixed value pasted into the configuration does not serve this purpose.
Start with a report-only policy in a test environment and follow the entire journey from opening the site to submitting a form or completing a purchase. This will reveal which permissions are genuinely needed before CSP starts blocking resources for users.
See also: What is MCP?
