DEV Community

Shai Alon
Shai Alon

Posted on • Originally published at on

10 tips to build a Content Security Policy (CSP) without breaking your site!

There are many benefits to adopting CSP — from blocking attacks (such as XSS), to improving security posture and compliance. However, many developers that try to use CSP — lack the experience and tooling to do so effectively — creating polices that may offer lower protection, and worse — block legitimate parts of their web applications —  breaking functionality in production!

So many developers get reports of their site getting broken — only to check and see this dreaded error:

**Refused to load the script**'...' **because it violates the following Content Security Policy directive** : "script-src 'self' 'report-sample'". Note that 'script-src-elem' was not explicitly set, so 'script-src' is used as a fallback.
Enter fullscreen mode Exit fullscreen mode

The below 10 tips, learned from years of deploying CSP at scale — will take you from A to Z with regards to proper deployment of Content-Security-Policy without breaking your website / web application.

We’ve baked these best practices into the RapidSec product — especially in the Security Manager that help you generate your CSP policies and maintain the versions over time. However, while some of the tips below refer to RapidSec’s features — since they are derived from an inherent need, you could very well use them independently.

1. Always start out with a strict Report-Only policy to map and learn your site

Many developers start out directly setting the Content-Security-Policy http header - in a staging site, or even production! This approach has the undesirable side-effect of blocking legitimate assets that load in your site. The correct approach is to start out with a Content-Security-Policy-Report-Only Header, that merely warns of CSP violations, but does not block them in practice.

By using this learning policy first, you can start out with a very strict policy, and send violation reports to your report-uri endpoint - simulating an enforced policy, and helping to map-out the structure of your site in order to later create a powerful CSP.

Start your process with a report-only LEARNING policy, which is not actually blocking anything. Namely - at this step you WANT to actually create more CSP reports, used by a generator to create a CSP automatically.

An initial bare policy for mapping out your site will look something like this:


HEADER VALUE (prettified):
**block-all-mixed-content** ;
**script-src**'self' 'report-sample';
**style-src**'self' 'report-sample';
**report-uri** [\_SPECIFIC\_PROJECT\_DETAILS](
Enter fullscreen mode Exit fullscreen mode

This policy will simulate blocking out all external assets loaded into the site, and send the reports to RapidSec (which will generate a CSP with this information). For stricter deployments, you can change 'self' (which is CSP for same-origin) with 'none' which will create more reports.

2. Don’t use the head tag to set the policy

Some legacy tutorials suggest adding the CSP inside your html

as a meta tag - like so:

The meta approach is counter-productive and has multiple drawbacks, including:

  • The Content-Security-Policy-Report-Only header is not supported, so you would be breaking rule #1 of starting out in report-only.
  • The CSP report-uri directive is not supported in meta, so you are flying blind basically.
  • Other Important directives like frame-ancestors or sandbox are not supported in meta.
  • The meta header CSP is generally unmaintained and causes many bugs in various browsers.

Some of the CSP errors that you may encounter when deploying your policy via meta tags:

The Content Security Policy directive 'report-uri' is **ignored when delivered via a <meta> element**.

The report-only Content Security Policy 'default-src 'none'; script-src 'self'; report-uri /violations' **was delivered via a <meta> element, which is disallowed. The policy has been ignored**.
Enter fullscreen mode Exit fullscreen mode

Solution: The preferred delivery mechanism for setting CSP (either enforced or report-only) is via HTTP headers. Btw — you can also set multiple CSPs, or one policy running in Report-Only, while another one is enforced. More on that, later.

Note: In some rare instances you cannot set HTTP header (some hosted platforms restrict this), at which — your only option would be to set a meta header if you wish to leverage CSP.

3. Maintain versions of your Content Security Policies

Without maintaining proper versions, it’s really hard to control your CSP policies at scale, and understanding which CSP, created which report. Think maintaining code without proper version control and accompanying platform like github!

Version control is critical with maintaining a CSP — and your setup should make sure that you know exactly which policy created which report.

4. Integrate CSP into your environment or CI/CD (rather than hard-code it)

Setting the headers dynamically can be quite challenging to maintain. Seeing this need, we created various plugins to support the needs of deployments to multiple environments.


There is also the option of manually maintaining the Security headers deployment, while still maintaining version control in RapidSec. We allow to pull the CSP and other config via API, and push changes via Webhooks — features that many of our enterprise customers use to integrate into their CI/CD.

More RapidSec Integration Options

5. Allow CSP Packages for known services

OK great — so you integrated you report-only CSP, and are now getting some report logs, only to discover that the majority of Content-Security-Policy reports sent to your CSP gateway, are actually not related to a part of your code / infrastructure, but rather generated by services used by your site. These 3rd party services can be impossible to reverse-engineer yourself — as some scripts have dynamic conditions that only get loaded for specific users.

Lets say for example that you use intercom for chat and support in your web application. If you only used your own reports to generate you CSP, it would take forever to discover that intercom actually requires DOZENS of custom directives to properly work with CSP.

That is why we invested many resources in creating crowdsourced CSP packages, that contain pre-built policies for the top 65 services on the internet. We’re also growing and actively maintaining this list.

RapidSec automatically detects the packages associated with you CSP reports, and the CSP Generator (Security Manager) will automatically suggest the needed policies. You just have to click “Allow”!

Allowing packages in the RapidSec CSP Generator

By easily clearing the noise and allowing known packages, you’re setting yourself up for success — leaving you to make decisions only on the actual custom code in your site.

This also saves you a lot of money on useless requests, as most CSP log service providers charge “per request” (and we also have a data-point limitation).

6. Avoid an overkill with the allow-list specifics

Some approaches suggest allowing highly custom CSP directives like:

img-src ...;
However, by explicitly allowing only a specific image - you would be forced to maintain a very large list - especially if this is a marketing site with widely changing content. In fact, our experience has shown this task to be especially hard!

Examples of use cases in which this scenario would cause the site to break, with the content-security-policy (CSP) blocking legitimate parts of the site:

  • The marketer has changed the image to:, but the CSP was not updated.
  • There is a different view (say responsive) that was introduced:, but this only gets rendered on very wide screens, so was not collected in initial configuration of the policy.

The best setting in this case would be to allow the entire CDN:
img-src ...;

RapidSec CSP Generator, has warnings when you create rules that are too strict and may end up breaking your site, or create excessive logs from legacy browsers:

Example of path based rules warning in RapidSec

7. Custom fit your CSP directive choice to the Web asset

Not all websites are created equal! Think of implementing CSP on the below site types:

  • Public content site — with dynamic injection of ad scripts. Requires a basic policy without using content directives or a default-src, with loosened enforcement.
  • General marketing site — with a few 3rd party scripts / ad services. Requires a compact policy without enforcing the content directives or default-src.
  • Web application requiring login — since these applications tend to have mission-critical data, or permissions — they required a strict policy with full enforcing of all directives.

Obviously there are more cases — some sites only use content-security-policy-report-only due to the varying nature of the site and content. Other sites we've seen using RapidSec (like payment gateways) - have such strong policies that they don't even allow external packages, with everything loading through the same-origin ('self' in CSP language).

Examples of how these setups make look in the RapidSec Security Manager, from the least strict, to the more strict.

If you have a mission critical site that you’re creating a CSP for — don’t worry!

Due to the architecture and lower dependency on 3rd parties in Web applications (rather than sites) — It’s actually easier to generate a strict content security policy for them without breaking the app, than it is to create a basic policy for a widely public site (thought that is also possible and effective with RapidSec).

So where does your project stand?

8. Enforce less strict versions of your reporting policy

As mentioned above — in some cases we won’t enforce all the directives. For the directives that we do enforce — another great technique to avoid blocking legitimate content when enforcing your CSP is switching it to a less strict version of your reporting policy.


Say that your Report-Only policy has:

script-src 'report-sample' [](
Enter fullscreen mode Exit fullscreen mode

You could run the enforced CSP in a less strict mode by adding 'self', dropping the https:// protocols, consolidating the Youtube / Cloudinary directives to *, and allowing unsafe-eval .

The policy might look something like this:

script-src 'self' 'report-sample' 'unsafe-eval' * *
Enter fullscreen mode Exit fullscreen mode

This way — you are enjoying the best of both worlds. Still enforcing a great policy, while running the stricter version in Report-Only, connected to a report-uri that will collect the violations (and hopefully generate meaningful alerts when needed).

RapidSec makes this rollup of the directives seamless — see an example from the RapidSec Security Manager on the script-src directive:

9. Keep Calm and Report-Only

We often see the case of developers being too eager to roll out an enforced policy — rolling it out enforced after having run only locally, or on real traffic in reporting mode for less than a day.

Don’t make this mistake — instead take your time!

You should have at least several days of pure reporting policy data on real traffic before combining enforcement.

Some things that get missed when moving to enforcement too soon:

  • Geography specific features — for example eu subdomains of services - that load dynamically in some regions.
  • User specific features — applications may expose some features only to certain users (behind feature flags), so they are less likely to show up in initial reporting data.
  • Less frequently used features — Yes — you remember that legacy feature in your application that nobody uses, is written in a legacy technology, and gets loaded via iframe! Take the time to have users generate reports on this too.

10. Seek advice

Some developers waste days, or even weeks trying to tinker their Content Security Policy, while even testing half-baked enforced policies on production systems , blocking legitimate parts, and breaking mission critical functionality.

Instead, just seek expert advice, based on years of hands on experience — to swiftly overcome these initial challenges.

Our team at RapidSec is more than happy to help you build the best CSP without breaking your site — feel free to reach us here:

Book a RapidSec Demo — Learn how RapidSec manages CSP (and other security headers) end-to-end to meet your security posture goals easily.

Schedule an Onboarding / Support call (RapidSec customers only) — Plan and roll out CSPs for many sites in your ORG without breaking functionality.

Or just contact us if you have any questions.

Bonus Tip: add nonces separately (later)

There are some discussions on wether you should start with a nonce / hash based CSP or an allowlist based CSP - I believe that the Allowlist approach is the best way to get the major benefits of CSP!

Using nonce / strict-dynamic / hash requires major refactoring of your web application / site code, and in many cases changing serving infrastructure to support this setup (that does not jive well with cached sites).

Moreover, nonce is only supported in script/style directives, is focused on XSS and does not offer any protection against Clickjacking, Formjacking.

Hence, it’s best to use an Allowlist policy , generated via the report-uri logs. For mission-critical assets with high XSS risk, add separate policies with nonce / hash / trusted-types .

Conclusion: How to build a Content Security Policy CSP without breaking this site

CSP deployment can be easy, if you incorporate the correct measures and planning to ensure success. You can Generate your Contnet-Security-Policy with RapidSec and have the 10 above tips “Baked-in” to your flow, but if you are taking another route — this knowledge is still highly valuable for you.

Any other suggestions / remarks? Feel free to Tweet / DM me @Shai_Alon , @rapid_sec

Originally published at on October 22, 2021.

Top comments (0)