Skip to content
MDView as Markdown

Content Security Policy

What is CSP?

Content Security Policy is a powerful security feature that allows you to take control of the resources your website is permitted to load and the actions it is allowed to take.

A Content Security Policy is delivered to the browser in an HTTP response header along with your page and the browser will then parse and enforce that policy. It can be used to mitigate serious security concerns like content-injection attacks, most notably Cross-Site Scripting (XSS), fix mixed-content and countless other benefits.

It's easy to get started with Content Security Policy and you can deploy it to your site in "report-only" mode which makes it completely safe to test.

If your site runs on a platform such as Adobe Commerce, Shopify or Dynamics 365 Commerce, or you deploy scripts through Google Tag Manager, check the Platforms guides too - most of them build the policy for you and just need your reporting endpoint. If you emit the header from your own application, the Frameworks guides cover nonce generation and header setup for Django, Rails, Laravel, Express, Next.js and more.

Getting Started

If you want to get started with CSP you can deploy a policy to use the CSP Wizard with no risk and no chance of breaking anything on your site. The suggested policy for getting started with the CSP Wizard, along with the two Reporting API headers it reports through, is:

Content-Security-Policy-Report-Only: default-src 'none'; script-src 'report-sha256'; form-action 'none'; frame-ancestors 'none'; report-uri {REPORT_URI}; report-to wizard
Reporting-Endpoints: default="{REPORTING_API_URL}", wizard="{REPORTING_API_URL}/wizard"
Report-To: {"group":"default","max_age":31536000,"endpoints":[{"url":"{REPORTING_API_URL}"}],"include_subdomains":true},{"group":"wizard","max_age":31536000,"endpoints":[{"url":"{REPORTING_API_URL}/wizard"}],"include_subdomains":true}

To deploy these headers on your website, you will need to add your own values to the example. You can find them on the Setup page in your account: set the 'Disposition' to 'Wizard' and copy the reporting directives and the Reporting API headers, then deploy the headers on your website. report-uri and report-to both send reports to the Wizard, and a browser uses only one of them, so you will not receive duplicate reports. 'report-sha256' sends CSP Integrity data about every script that runs on your pages through the Reporting API. Those reports count towards your quota at the sampled rate. Here are some examples on how to do that depending on your platform or language of choice. Please note that your values are unique to you, and you should not use those found in the examples here:

PHP
header("Content-Security-Policy-Report-Only: default-src 'none'; script-src 'report-sha256'; form-action 'none'; frame-ancestors 'none'; report-uri https://demo.report-uri.com/r/d/csp/wizard; report-to wizard");
header('Reporting-Endpoints: default="https://demo.report-uri.com/a/d/g", wizard="https://demo.report-uri.com/a/d/g/wizard"');
header('Report-To: {"group":"default","max_age":31536000,"endpoints":[{"url":"https://demo.report-uri.com/a/d/g"}],"include_subdomains":true},{"group":"wizard","max_age":31536000,"endpoints":[{"url":"https://demo.report-uri.com/a/d/g/wizard"}],"include_subdomains":true}');
Nginx
add_header Content-Security-Policy-Report-Only "default-src 'none'; script-src 'report-sha256'; form-action 'none'; frame-ancestors 'none'; report-uri https://demo.report-uri.com/r/d/csp/wizard; report-to wizard";
add_header Reporting-Endpoints 'default="https://demo.report-uri.com/a/d/g", wizard="https://demo.report-uri.com/a/d/g/wizard"';
add_header Report-To '{"group":"default","max_age":31536000,"endpoints":[{"url":"https://demo.report-uri.com/a/d/g"}],"include_subdomains":true},{"group":"wizard","max_age":31536000,"endpoints":[{"url":"https://demo.report-uri.com/a/d/g/wizard"}],"include_subdomains":true}';
Apache
Header set Content-Security-Policy-Report-Only "default-src 'none'; script-src 'report-sha256'; form-action 'none'; frame-ancestors 'none'; report-uri https://demo.report-uri.com/r/d/csp/wizard; report-to wizard"
Header set Reporting-Endpoints "default=\"https://demo.report-uri.com/a/d/g\", wizard=\"https://demo.report-uri.com/a/d/g/wizard\""
Header set Report-To "{\"group\":\"default\",\"max_age\":31536000,\"endpoints\":[{\"url\":\"https://demo.report-uri.com/a/d/g\"}],\"include_subdomains\":true},{\"group\":\"wizard\",\"max_age\":31536000,\"endpoints\":[{\"url\":\"https://demo.report-uri.com/a/d/g/wizard\"}],\"include_subdomains\":true}"
IIS

Open IIS Manager and navigate to the level you want to manage, In Features View, double-click HTTP Response Headers. On the HTTP Response Headers page, in the Actions pane, click Add. In the Add Custom HTTP Response Header dialog box use the following name and value and then click OK. Repeat for each of the three headers.

Name: Content-Security-Policy-Report-Only

Value: default-src 'none'; script-src 'report-sha256'; form-action 'none'; frame-ancestors 'none'; report-uri https://demo.report-uri.com/r/d/csp/wizard; report-to wizard

Name: Reporting-Endpoints

Value: default="https://demo.report-uri.com/a/d/g", wizard="https://demo.report-uri.com/a/d/g/wizard"

Name: Report-To

Value: {"group":"default","max_age":31536000,"endpoints":[{"url":"https://demo.report-uri.com/a/d/g"}],"include_subdomains":true},{"group":"wizard","max_age":31536000,"endpoints":[{"url":"https://demo.report-uri.com/a/d/g/wizard"}],"include_subdomains":true}

Tuning Your Policy

The policy above is great to get started but will generate a lot of reports so you need to fine-tune your policy based on these reports. Take the following report as an example:

Screenshot

This report informs us that on our homepage, https://example.com/, we have a script that is being loaded from https://cdnjs.com. If you do have a script on your homepage that you want to load from this domain then you need to update your policy to allow that. The domain needs adding to the script-src directive to do that.

script-src 'self' cdnjs.com

This update to the script-src directive now includes the self keyword as we had before but it now also includes the cdnjs.com domain which lets the browser know it is allowed to load scripts from that domain. Once you update your policy you will no longer receive reports about that script as it will be allowed.

If the report highlights a problem and you do not want to allow the resource to load, do not add the value to your policy. Instead you must find the asset on the page and remove it or load it from a whitelisted location instead.

With each update you make to your policy you will receive fewer and fewer reports as you whitelist the appropriate items or remove/change resources that you don't want to be loaded. We recommend that you work through tuning your policy as quickly as possible to reduce the number of reports you send and thus the cost incurred on your account. You can also enable/disable reporting during specific periods of monitoring to help reduce your volume during the early stages of policy tuning.

Reporting Directives

If you already have an existing Content Security Policy, or just want to enable reporting in either a CSP or CSPRO header without using the CSP Wizard, you can find your reporting directives on the Setup page. We recommend using both report-uri and report-to:

report-uri {REPORT_URI}; report-to default

Your values are unique per-user and per-team, so make sure you use the ones obtained from logging in to your account.

report-to sends reports through the Reporting API, so it needs the Reporting API headers on your site as well. It is also the only way CSP Integrity data is sent: add 'report-sha256' to your script-src directive to collect it. You do not need to indicate the disposition of the policy (enforced or report only) with report-to, as this is taken care of automatically.

report-uri is the traditional mechanism, for browsers that do not support report-to. If the browser supports report-to then reports will be sent via the Reporting API and report-uri is ignored, so setting both will not send duplicate reports. When using report-uri it is important to use the correct path, which indicates the disposition of the policy delivered to the browser by your website. You must use the /enforce path in a CSP header report-uri directive and the /reportOnly path in a CSPRO header report-uri directive.

Filtering Reports

You can configure filters for your inbound CSP reports in the Filters section in your account. These filters allow you to reduce the amount of noise and make it easier to find reports that matter.

We recommend keeping the default set of filters enabled to keep your report data manageable. Those are 'Remove "referrer" value', 'Remove query string', 'Filter violations caused by browser extensions' and 'Filter unactionable CSP violations'. Filtering reports with an 'about' or 'data' blocked-uri will depend on your specific needs.

The 'Sites to collect reports for' field must be filled in and informs us of the domain names you expect to receive reports from. This is a space-separated list of domain names.

For more details on the CSP Wizard check out this blog post: https://scotthelme.co.uk/report-uri-csp-wizard/

https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP

https://developers.google.com/web/fundamentals/security/csp/

https://en.wikipedia.org/wiki/Content_Security_Policy

https://scotthelme.co.uk/content-security-policy-an-introduction/

https://www.w3.org/TR/CSP3/

https://www.w3.org/TR/CSP2/

https://scotthelme.co.uk/csp-cheat-sheet/

https://www.troyhunt.com/the-6-step-happy-path-to-https/

https://www.troyhunt.com/understanding-csp-the-video-tutorial-edition/

https://www.troyhunt.com/locking-down-your-website-scripts-with-csp-hashes-nonces-and-report-uri/

Start Monitoring with Report URI

Already using us for CSP? Find your CSP reporting directives on Setup, or view your CSP reports.

New to Report URI? Create an account and grab your CSP reporting directives from the Setup page. Prefer a guided setup? The CSP Wizard builds a policy from your reports automatically once you're signed in.

Start your free trial