CSP Evaluator: How to Audit Your Content-Security-Policy Header
A CSP evaluator tells you what's wrong with your Content-Security-Policy on paper. Monitoring tells you what it actually blocks in production. Use both.
Static evaluators find weak directives. Monitoring finds real-world breakage.
- Run an 8-point audit on your existing CSP.
- Compare Google CSP Evaluator, csper.io, and CSPify monitoring.
- Mirror evaluator findings against report-only data in production.
- Set an evaluation cadence so policy drift never wins.
When auditing your web application’s security posture, a CSP evaluator is your first line of defense against misconfigurations. Static evaluators tell you “this directive is weak,” highlighting syntax errors, overly permissive wildcards, and known bypasses in your Content-Security-Policy header. But they can’t see what your policy blocks in production.
If you’re relying solely on a static scan, you are only seeing half the picture. The ideal workflow combines the immediate feedback of a static CSP evaluator with the continuous visibility of runtime monitoring. In this guide, we will walk through an 8-point audit checklist to harden your policy, compare popular tools like Google CSP Evaluator and csper.io, and show you how to evaluate your CSP safely without breaking production traffic. If you’re starting from scratch, check out our CSP generator guide first.
What a CSP evaluator does (and what it doesn’t)
A CSP evaluator is a static analysis tool. You paste your Content-Security-Policy header into the tool, and it parses the directives, checks them against a database of known security anti-patterns, and returns a list of warnings or errors. It is an essential step in the CSP lifecycle, ensuring that your policy actually provides the security guarantees you think it does.
Evaluators are incredibly good at catching human error. They will immediately flag if you’ve accidentally allowed unsafe-inline in your script sources, if you’re missing a fallback directive, or if you’ve typoed a keyword. They act as a linter for your security headers, enforcing best practices defined by the W3C CSP3 spec and the OWASP CSP Cheat Sheet.
Static analysis vs. runtime monitoring (the gap)
However, a static CSP evaluator has a fundamental limitation: it operates in a vacuum. It doesn’t know what scripts your application actually needs to run. It doesn’t know if a third-party marketing tag dynamically injects an inline style. It doesn’t know if a browser extension is injecting content that your policy will block.
This is the gap between static analysis and runtime monitoring. A static evaluator might tell you that script-src https://cdn.example.com is a valid and secure directive. But if your application actually relies on a script hosted at https://assets.example.com, the evaluator won’t warn you that your policy will break your site. It only evaluates the policy itself, not the policy’s interaction with your live application.
To truly secure your application without causing outages, you need both. You use a static evaluator to ensure the policy is structurally sound and free of known bypasses. You use runtime monitoring to ensure the policy accommodates your application’s actual behavior and to detect when that behavior changes.
The CSP audit checklist
Before you deploy any Content-Security-Policy, it should pass a rigorous audit. Whether you use an automated CSP evaluator or review the policy manually, these are the eight critical checks every policy must pass.
1. Has default-src defined?
The default-src directive acts as a fallback for most other fetch directives. If you omit default-src, and you also omit a specific directive like font-src, the browser will allow fonts to be loaded from anywhere. This defeats the purpose of an allowlist.
Weak:
Content-Security-Policy: script-src 'self'; style-src 'self';
Strong:
Content-Security-Policy: default-src 'none'; script-src 'self'; style-src 'self';
Always start with default-src 'none' and explicitly allow only what is necessary.
2. Are wildcards used in script-src / connect-src?
Wildcards (*) are dangerous, especially in script-src and connect-src. Allowing script-src https://*.example.com means that if an attacker compromises any subdomain of example.com, they can execute arbitrary JavaScript on your site.
Weak:
Content-Security-Policy: default-src 'none'; script-src https://*.cdn.com;
Strong:
Content-Security-Policy: default-src 'none'; script-src https://scripts.cdn.com;
Be as specific as possible. If you must use wildcards due to dynamic infrastructure, consider using strict-dynamic explained in combination with nonces or hashes to restrict execution to trusted scripts, regardless of their origin.
3. Is unsafe-inline or unsafe-eval present?
The primary goal of CSP is to mitigate Cross-Site Scripting (XSS). Allowing 'unsafe-inline' in your script-src completely undermines this goal, as it allows attackers to inject <script> tags directly into the HTML. Similarly, 'unsafe-eval' allows the execution of strings as code via eval(), which is a common vector for DOM-based XSS.
Weak:
Content-Security-Policy: default-src 'none'; script-src 'self' 'unsafe-inline' 'unsafe-eval';
Strong:
Content-Security-Policy: default-src 'none'; script-src 'self' 'nonce-rAnd0m123';
Instead of 'unsafe-inline', use CSP nonces or CSP hash sources to explicitly allow specific inline scripts.
4. Is frame-ancestors set (clickjacking)?
The frame-ancestors directive protects your site against clickjacking attacks by controlling which origins can embed your site in an <iframe>, <frame>, <object>, or <embed>. It obsoletes the older X-Frame-Options header.
Weak:
Content-Security-Policy: default-src 'none'; script-src 'self';
Strong:
Content-Security-Policy: default-src 'none'; script-src 'self'; frame-ancestors 'none';
If your site should never be framed, set it to 'none'. If it should only be framed by your own origin, set it to 'self'.
5. Are report-uri and report-to set?
A CSP without reporting is flying blind. When a violation occurs, the browser needs to know where to send the telemetry. Without report-uri (or the newer report-to), you will never know if your policy is blocking legitimate traffic or if an attacker is actively probing your defenses.
Weak:
Content-Security-Policy: default-src 'none'; script-src 'self';
Strong:
Content-Security-Policy: default-src 'none'; script-src 'self'; report-uri https://api.cspify.io/report/your-project-id;
6. Is object-src 'none' set?
The <object>, <embed>, and <applet> elements can be used to load plugins like Flash or Java, which have historically been a massive source of security vulnerabilities. Even if you don’t use these elements, an attacker might inject them.
Weak:
Content-Security-Policy: default-src 'none'; script-src 'self';
Strong:
Content-Security-Policy: default-src 'none'; script-src 'self'; object-src 'none';
Unless you have a very specific, legacy requirement, object-src should always be 'none'.
7. Is base-uri 'self' or 'none' set?
The <base> element specifies the base URL to use for all relative URLs contained within a document. If an attacker can inject a <base> tag, they can hijack relative URLs (like scripts or stylesheets) to point to their own malicious servers.
Weak:
Content-Security-Policy: default-src 'none'; script-src 'self';
Strong:
Content-Security-Policy: default-src 'none'; script-src 'self'; base-uri 'none';
Restrict base-uri to 'self' or 'none' to prevent base tag hijacking.
8. Is the policy too long (split header risk)?
While not strictly a security vulnerability in the policy itself, excessively long CSP headers can cause operational issues. Some older servers or proxies may truncate headers that exceed certain length limits (often 4KB or 8KB). If your CSP is truncated, the browser may fail to parse it, leaving your site unprotected, or it may parse a partial policy, leading to unexpected breakages.
If your policy is growing too large, it’s usually a sign that you are relying too heavily on massive allowlists of domains. Consider refactoring to use nonces or hashes, which scale much better than origin-based allowlists.
Google CSP Evaluator: how it works and when it falls short
The Google CSP Evaluator is the industry standard for static CSP analysis. Built by Google’s security team, it is a fast, free, and highly effective tool for identifying structural weaknesses in your policy.
What Google’s tool catches well
Google’s evaluator excels at identifying bypasses. It maintains a comprehensive database of JSONP endpoints and angular libraries hosted on popular CDNs. If you allowlist a CDN that hosts a known vulnerable endpoint, the evaluator will flag it.
For example, if you input this policy:
Content-Security-Policy: default-src 'none'; script-src 'self' https://cdnjs.cloudflare.com;
The Google CSP Evaluator will immediately warn you: “cdnjs.cloudflare.com is known to host JSONP endpoints which allow to bypass this CSP.” This is invaluable insight that is difficult to maintain manually. It also reliably catches missing fallback directives, the presence of 'unsafe-inline', and overly broad wildcards.
What it misses (runtime drift, third-party changes)
However, Google’s tool is purely static. It evaluates the policy exactly as you paste it. It cannot account for runtime drift.
Imagine you evaluate your policy today, and it passes with flying colors. Tomorrow, your marketing team adds a new tracking pixel via Google Tag Manager. That pixel attempts to load a script from a new domain not in your CSP. The Google CSP Evaluator will not alert you to this change. Your site will simply break for users, or the tracking pixel will fail to fire, and you won’t know until someone complains.
Furthermore, the evaluator cannot verify if the directives you’ve specified actually match your application’s requirements. It can tell you if a directive is secure, but it cannot tell you if it is correct for your specific context.
csper.io evaluator vs. CSPify monitoring
When looking beyond Google’s tool, you’ll often encounter alternatives like the csper.io evaluator and comprehensive monitoring solutions like CSPify. Understanding the difference between a static scanner and a runtime monitoring platform is crucial for maintaining a healthy CSP.
| Feature | Google CSP Evaluator | csper.io Evaluator | CSPify Monitoring |
|---|---|---|---|
| Type | Static Analysis | Static Analysis | Runtime Monitoring |
| Cost | Free | Free (basic) / Paid | Paid (with Free tier) |
| Runtime Data | No | No | Yes |
| Multi-App Support | No | Yes | Yes |
| Alerting | No | No | Yes (Slack, Email, Webhooks) |
| Data Retention | None | None | Up to 90 days |
| Suggested For | Initial policy design, quick checks | Policy design, quick checks | Production deployment, continuous enforcement |
While csper.io provides a robust static evaluator, it suffers from the same fundamental limitation as Google’s tool: it cannot see what is happening in production. CSPify does not replace these static evaluators; it complements them. You use a static evaluator to build a secure policy, and you use CSPify to ensure that policy doesn’t break your site and remains effective over time.
How to evaluate a CSP without breaking it
Deploying a strict CSP directly to production is a recipe for disaster. Even with rigorous static evaluation, you will inevitably miss something. The safe deployment path involves a phased approach, utilizing both static tools and runtime telemetry.
Run it through a static evaluator first
Before a policy ever touches a server, run it through a static evaluator. This is your sanity check. It ensures you haven’t made any glaring syntax errors or introduced known bypasses.
Let’s look at a typical “before” policy that an evaluator would tear apart:
Content-Security-Policy: script-src 'self' 'unsafe-inline' https://*.google-analytics.com; style-src 'self' 'unsafe-inline'; font-src *;
Evaluator output narrated:
- Missing
default-src: The evaluator will flag thatdefault-srcis missing, meaning fetch directives likeconnect-srcorimg-srcare completely unrestricted. unsafe-inlineinscript-src: This is a critical error. It allows arbitrary JavaScript execution, rendering the CSP largely ineffective against XSS.- Wildcard in
script-src: Allowinghttps://*.google-analytics.comis overly broad. unsafe-inlineinstyle-src: While less critical than scripts, it still allows CSS injection attacks.- Wildcard in
font-src: Allowing fonts from anywhere (*) is unnecessary and increases the attack surface.
Here is the “after” policy, revised to pass the static evaluator:
Content-Security-Policy: default-src 'none'; script-src 'self' 'nonce-rAnd0m123' https://www.google-analytics.com; style-src 'self' 'nonce-rAnd0m123'; font-src 'self' https://fonts.gstatic.com; connect-src 'self' https://www.google-analytics.com; img-src 'self' data:;
Mirror it as Content-Security-Policy-Report-Only in production
Once your policy passes the static evaluator, do not deploy it as an enforcing header. Instead, deploy it using the Content-Security-Policy-Report-Only header.
As detailed in our report-only mode guide, this header instructs the browser to evaluate the policy and report any violations, but not to block the resources. This allows you to observe the impact of your policy on real user traffic without causing any actual breakage.
Content-Security-Policy-Report-Only: default-src 'none'; script-src 'self' 'nonce-rAnd0m123' https://www.google-analytics.com; style-src 'self' 'nonce-rAnd0m123'; font-src 'self' https://fonts.gstatic.com; connect-src 'self' https://www.google-analytics.com; img-src 'self' data:; report-uri https://api.cspify.io/report/your-project-id;
Watch report volume by directive
With the policy in report-only mode, monitor the incoming violation reports in your CSPify dashboard. Look for spikes in volume for specific directives.
If you see thousands of script-src violations for a specific third-party domain, you know you need to add that domain to your allowlist (or implement strict-dynamic) before moving to enforcement. If you see violations for unsafe-inline styles, you need to identify where those styles are being injected and refactor them, or add nonces. Only when the violation volume drops to near zero for legitimate traffic should you switch to the enforcing Content-Security-Policy header.
Common findings every evaluator flags
When you first run an existing policy through an evaluator, you are likely to see a sea of red warnings. Here are the most common findings and how to address them.
'unsafe-inline' in script-src
This is the most common and most critical finding. It means your policy allows inline <script> tags and inline event handlers (like onclick).
The Fix: Remove 'unsafe-inline'. Refactor inline scripts into external .js files. For scripts that must remain inline, use CSP nonces (a unique, cryptographically strong random value generated on each request) or CSP hash sources (the SHA-256 hash of the script content).
data: in script-src
Allowing data: URIs in script-src is dangerous because attackers can inject malicious scripts encoded as data URIs.
The Fix: Remove data: from script-src. If you must use data URIs, restrict them to img-src (for inline images) or font-src (for inline fonts), where they pose a significantly lower risk.
Third-party CDN with no SRI
If you allowlist a broad CDN (like https://cdnjs.cloudflare.com), evaluators will warn you that an attacker could potentially load an old, vulnerable version of a library from that CDN to bypass your security.
The Fix: While you can’t always avoid CDNs, you should mitigate the risk by using Subresource Integrity (SRI) on your <script> tags. SRI ensures that the browser only executes the script if its content matches a known cryptographic hash, preventing attackers from modifying the file on the CDN.
Wildcard *.example.com
Wildcards are convenient but overly permissive. If any subdomain under example.com is compromised, your CSP is compromised.
The Fix: Be specific. List the exact subdomains you need (e.g., api.example.com, assets.example.com). If managing a large list of subdomains becomes unwieldy, look into modern CSP features like 'strict-dynamic', which allows you to trust scripts based on a nonce or hash, and then transitively trust any scripts they dynamically load, reducing the need for massive origin allowlists.
Building an evaluation cadence (not a one-off)
Evaluating your CSP is not a one-time task. Web applications evolve. Marketing teams add new trackers. Developers integrate new third-party services. If you don’t continuously evaluate your policy, it will suffer from policy drift, eventually breaking your site or leaving it vulnerable.
You must establish an evaluation cadence.
- Continuous Monitoring: Use a tool like CSPify to monitor your
report-uriendpoint 24/7. Set up alerts for unexpected spikes in violations, which often indicate a new deployment that violates the policy or an active attack. - Pre-Deployment Checks: Integrate a static CSP evaluator into your CI/CD pipeline. Fail the build if a developer attempts to commit a policy that introduces
'unsafe-inline'or removesdefault-src. - Monthly Audits: Schedule a manual review of your CSP every month. Review the top violation reports in CSPify. Are they noise (like browser extensions)? Or are they legitimate application features that need to be allowlisted? Use this data to refine and tighten your policy.
By combining the immediate feedback of a static evaluator with the continuous visibility of runtime monitoring, you can build a Content-Security-Policy that is both highly secure and operationally resilient.
Frequently asked questions
- Is Google CSP Evaluator free? Yes, Google CSP Evaluator is a free, open-source static analysis tool provided by Google to help developers identify security weaknesses in their Content-Security-Policy headers.
- What is the best alternative to Google CSP Evaluator? While csper.io offers a comparable static evaluator, the best alternative workflow is to pair a static evaluator with a runtime monitoring solution like CSPify. Static tools find syntax errors, while monitoring finds real-world breakage.
- Can a CSP evaluator detect runtime issues? No. A static CSP evaluator only analyzes the text of the policy itself. It cannot see what resources your application actually attempts to load in a live browser environment, which is why runtime monitoring is essential.
- What is the difference between CSP Evaluator and CSP Generator? A CSP Generator helps you build a new policy from scratch by providing a UI to select directives. A CSP Evaluator analyzes an existing policy to find security flaws, weak directives, and syntax errors.
- Does CSPify replace Google CSP Evaluator? No, it complements it. You should use Google CSP Evaluator to ensure your policy is structurally secure, and use CSPify to monitor the policy in production to ensure it doesn’t break your application.
- How often should I re-evaluate my CSP?
You should monitor your CSP continuously using a
report-uriendpoint. Additionally, you should run a static evaluation in your CI/CD pipeline on every deployment, and perform a manual audit of your policy and violation reports at least monthly. - Can csper.io evaluator audit a
report-onlypolicy? Yes. Static evaluators analyze the directives within the policy string. They do not care whether the header is namedContent-Security-PolicyorContent-Security-Policy-Report-Only. - What does an evaluator mean by “weak directive”?
A weak directive is one that fails to provide meaningful security. Common examples include using
'unsafe-inline'(which allows XSS), using overly broad wildcards (like*.com), or missing adefault-srcfallback.
Ready to move beyond static analysis? Build a CSP from scratch with our builder, or Get CSP monitoring to see exactly what your policy is blocking in production.
Pair your evaluator with real production data
Start Starter to layer monitoring, retention, and alerting on top of your evaluated CSP.