A missing Content Security Policy leaves browsers without guidance on which scripts and resources to trust. That gap exposes your application to cross-site scripting (XSS), script injection, and other unwanted third-party behavior. This guide explains what the issue means, why it matters, and how to fix it across common web servers and application frameworks without breaking functionality.

A Content Security Policy is an HTTP response header that tells browsers which scripts, styles, frames, and other resources can load on a page. By restricting what is allowed to execute, CSP reduces the risk of cross-site scripting (XSS) and code injection, especially in modern environments filled with third-party code.
A robust CSP acts as a guardrail for runtime behavior. Even if an attacker finds an injection point, a restrictive policy can stop their payload from running.
If your application does not send a Content-Security-Policy header, the browser has no information about content source restrictions to enforce. All inline scripts, external scripts, and frames are treated as allowed unless blocked by some other mechanism. From a security standpoint, running an app like that is equivalent to driving without seatbelts.
Without CSP enforcement, an attacker has more freedom to introduce untrusted scripts through reflected, stored, or DOM-based XSS. This can lead to session hijacking, phishing overlays, data exfiltration, or tampering with legitimate page content. Third-party scripts that load from advertising networks, analytics tools, or widgets also become a higher-risk surface because they can be replaced upstream with malicious versions.
CSP can also help restrict framing and mitigate clickjacking when combined with frame-ancestors. While it is not a complete substitute for X-Frame-Options, modern deployments often rely on CSP as the primary layer of protection.
Real-world incidents frequently involve untrusted scripts injected into compromised libraries or advertising channels. Without CSP, these attacks run silently and can persist until discovered through other means.
Dynamic application security testing (DAST) is the most reliable way to spot missing or ineffective security headers at scale because it observes the running application. A modern application security platform can check every reachable page and surface inconsistent header behavior or areas where CSP has not been applied. In CI/CD environments, header validation helps catch regressions before they ship, while browser developer tools provide a quick manual check. If an application includes XSS vulnerabilities, a DAST with proof-based scanning further validates whether they are exploitable and whether a CSP would have an effect on exploitability.
To resolve this issue, configure your application or web server to send a Content-Security-Policy header with restrictive directives. Start with a minimal policy to enable enforcement, then tune it to match real application needs.
Two example policies are included in this guide. The first is a minimal valid CSP to only allow content from your own site (from the same origin):
default-src 'self';Most examples in this guide here show this minimal version to keep things short. In practice, though, most sites and web applications require external content loaded from CDNs and other sources, so your CSP needs to account for that. Here’s a more realistic production CSP that you can customize to your specific needs:
default-src 'self';
script-src 'self' https://trusted.cdn.example.com 'nonce-{{RANDOM_VALUE_HERE}}';
style-src 'self' https://fonts.googleapis.com;
font-src 'self' https://fonts.gstatic.com;
img-src 'self' data:;
connect-src 'self';
frame-ancestors 'self';
base-uri 'self';
object-src 'none';This production example assumes a typical structure with a content delivery network, required fonts and styles, and the use of nonces to manage inline scripts (see below for more about nonces). Real deployments will vary, but the general pattern is widely applicable. Note that object-src, though still widely used and supported, will be deprecated in CSP3.
Learn more about available CSP directives and values.
Content security policies can be set at several different levels:
Server-level configuration is the most reliable approach in most cases because it ensures that every response includes a CSP header, regardless of which part of the application generates it. This helps avoid gaps caused by routing differences or legacy endpoints that run outside the main framework and also makes central maintenance much easier.
Application-level CSP header generation offers more flexibility when you need dynamic nonces, route-specific directives, or programmatic control over complex rendering logic. For special cases or tightly scoped testing, you can apply a limited set of CSP directives using meta tags in the HTML itself, although this is less secure because it takes effect only after the page begins loading.
In practice, most organizations use a combination of server-level policies for broad coverage and framework-level policies where dynamic behavior or fine-grained control is needed. CSP headers rather than meta tags should be used wherever possible.
Setting any CSP header at the server level requires a configuration change followed by careful testing to ensure that all legitimate resources continue to load.
To apply a minimal valid policy, add the following to your Nginx server config block:
add_header Content-Security-Policy "default-src 'self';" always;Note the additional always parameter – without this, the CSP header won’t be set on error responses (4xx, 5xx). Error pages are extremely common XSS targets, and omitting the always parameter would leave them unprotected by CSP.
For a more realistic deployment, replace the value with the expanded policy set shown earlier. Reload Nginx to apply the update.
First, ensure the headers module is enabled, then add a CSP header to your VirtualHost or main configuration:
<IfModule headers_module>
Header always set Content-Security-Policy "default-src 'self';"
</IfModule>As with Nginx, replace the minimal value with a broader policy when you are ready. Restart Apache to apply the changes.
Configure the header using the IIS Manager interface by opening HTTP Response Headers for the desired site and adding:
Content-Security-Policy default-src 'self'; You can substitute the production policy at any time. Restart the site if needed to ensure the header is applied consistently.
Some teams prefer to set CSP within application code so that the header is generated for every response. This approach is straightforward in modern frameworks and the only major difference is the function you call to set the header. Again, the examples only use the minimal valid policy for clarity. Your actual production CSP will be much longer, but it’s set in the same way.
For example, in Express, you would pass the header name and value to the set() method of the response:
res.set("Content-Security-Policy", "default-src 'self';");Doing this in Flask or Django is very similar but uses the headers array of the response:
response.headers['Content-Security-Policy'] = "default-src 'self';"For Java and Spring Security, you can append a filter or security configuration object to the header, for example:
@Configuration
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.headers()
.contentSecurityPolicy("default-src 'self';");
return http.build();
}
}Setting CSP headers at the application level is necessary if you use nonces to secure your inline script sources. A nonce means a “number used once,” and it’s used to provide a unique script identifier. At a minimum, note that the nonce must be:
Learn more about securing inline scripts using CSP.
If setting a CSP header is not practical, some CSP directives can be added using HTML meta tags:
<meta http-equiv="Content-Security-Policy" content="default-src 'self';">Meta-based CSP enforcement has several major limitations:
frame-ancestors, report-uri, report-to, or sandbox directives.Server-level headers remain the recommended approach for full protection.
Enforcing a Content Security Policy without preparation and testing can break legitimate functionality, especially when sites rely on inline scripts or external resources. A safe rollout includes a testing period and iterative tuning.
Report-only mode lets you identify policy violations without blocking functionality. This uses a separate Content-Security-Policy-Report-Only header that accepts identical values to the actual CSP header but only reports policy violations instead of blocking content. The two headers can be combined (sent together) if you want to test some new directive while still enforcing the existing policy.
A minimal report-only header looks like this, where report-to (previously report-uri) specifies the address to send policy violation reports to (these are POST requests in JSON format):
Content-Security-Policy-Report-Only: default-src 'self'; report-to /REPORT_PARSER_URI_HERE;While the report-to parameter is required for reporting to work, you don’t necessarily have to set up a dedicated parser. For small-scale work, you can simply observe the reports in your browser’s dev tools.
During the testing phase, you can compare expected behavior with actual violations and adjust directives until you’re ready to apply them in production.
Modern browsers log CSP issues in the developer console. Reviewing these messages provides insight into which scripts, frames, and connections need explicit allowances.
Tools such as Google’s CSP Evaluator help identify weak directives, overly broad allowances, or unsafe patterns. They are useful early in deployment, especially when policies grow complex.
A well-implemented CSP can greatly reduce the exploitability of many XSS vectors. It also supports control requirements in standards such as PCI DSS, SOC 2, HIPAA, and ISO 27001. Avoiding script-based compromise lowers the cost of incident response, especially for customer-facing applications that handle sensitive data.
Invicti’s DAST scanner makes it easy to identify missing or inconsistent CSP headers as part of routine scanning, so you do not have to rely on manual checks across different environments. Because the scanner tests the actual running application rather than the source code, it shows where headers are absent, overridden, or applied only on some routes to help track down configuration drift and gaps that are hard to see from the code alone.
When a scan identifies XSS issues, Invicti’s proof-based scanning will usually confirm whether they are exploitable and often also provide a one-click proof of concept reproducing the issue. This gives you a clearer picture of where a CSP may have the most impact and which findings require immediate attention.
For teams working across multiple environments, the Invicti platform also helps surface third-party script usage and API endpoints that might influence CSP decisions. Developers get actionable results that fit into existing workflows, which makes CSP fixes easier to prioritize and verify without slowing down release cycles.
CSP is one of the most impactful security headers you can deploy because it provides immediate protection against common script-based attacks. Once you fix a missing or overly permissive header and fine-tune your policy, you can fold CSP into broader security hardening efforts that improve resilience with relatively low overhead. Automated scanning, consistent enforcement, and well-defined governance help ensure CSP remains a dependable layer of defense as your applications evolve.
It means the application is not setting a Content-Security-Policy header to tell the browser which sources for scripts and other resources are permitted and which should be blocked.
CSP is one of the most powerful security headers. When set correctly, it can prevent a large proportion of cross-site scripting (XSS), code injection, data theft, and malicious script execution attempts by precisely controlling what resources a browser can safely load.
Add a Content-Security-Policy header in your web server config or application code to restrict permitted content sources. You can usually start with default-src 'self' to block all external content and then fine-tune the policy for your application. Start in report-only mode before enforcing the policy in production.
Not all, but a well-designed CSP can eliminate many XSS attack vectors and make others harder to exploit. It also limits the scope of any successful attacks.
Invicti’s DAST scanner flags missing or weak CSP headers and uses proof-based scanning to validate real XSS vulnerabilities, including those made possible by CSP gaps.