Introduction
In a previous article, I explained how modern web applications rely heavily on JavaScript, CSS, images, fonts, APIs, and third-party libraries. While these resources make applications more interactive, they also increase the attack surface.
And input validation and output encoding help reduce these risks, but they are not always sufficient. A single overlooked vulnerability can still allow malicious scripts to execute.
This is where Content Security Policy (CSP) comes in.
Content Security Policy (CSP) is an HTTP response header that instructs browsers which resources are trusted.
For example:
Content-Security-Policy:
default-src 'self';
This simple policy tells the browser:
Only load resources from the same origin.
Unlike many security mechanisms that operate on the server, CSP is enforced by the browser.
If a resource violates the policy, it is blocked.
How CSP Works
Every time the browser encounters a resource, it asks a simple question:
Does this resource satisfy the Content Security Policy?
For example:
![Capture d'écran 2026-08-03 235459]()
The same process applies to:
JavaScript, CSS, Images, Fonts, AJAX requests, WebSockets, iframes, Forms, Media files.
Adding CSP in ASP.NET Core
The simplest approach is using middleware.
app.Use(async (context, next) =>
{
context.Response.Headers.Append(
"Content-Security-Policy",
"default-src 'self';");
await next();
});
Now every response includes:
Content-Security-Policy:
default-src 'self';
Understanding CSP Directives
A CSP policy is made up of directives.
Each directive controls a different type of resource.
For example:
Content-Security-Policy:
default-src 'self';
script-src 'self';
style-src 'self';
img-src 'self';
Let's examine the most important directives.
default-src
The fallback directive.
If another directive isn't specified, the browser uses default-src.
Example:
default-src 'self'
Allows:
https://myapp.com/logo.png
https://myapp.com/app.js
https://myapp.com/site.css
Blocks:
https://cdn.example.com/script.js
script-src
Controls JavaScript execution.
script-src 'self'
Allowed:
<script src="/js/site.js"></script>
Blocked:
<script src="https://cdn.example.com/jquery.js"></script>
Unless the CDN is explicitly allowed:
script-src 'self'
https://cdn.example.com
style-src
Controls CSS.
style-src 'self'
Allows:
<link rel="stylesheet" href="/css/site.css">
Blocks:
<style>
body{
background:red;
}
</style>
unless hashes, nonces, or 'unsafe-inline' are used (will be explained later).
img-src
Controls image loading.
img-src 'self'
Allows:
/images/logo.png
Blocks:
https://images.example.com/logo.png
Need to allow a CDN?
img-src 'self'
https://images.example.com
font-src
Controls web fonts.
font-src 'self'
If using Google Fonts or another provider, you must explicitly allow it.
connect-src
Controls network connections initiated by JavaScript.
Includes: Fetch API, XMLHttpRequest, SignalR, WebSockets, EventSource.
Example:
connect-src 'self'
https://api.example.com
Report-Only Mode
Before enforcing a strict CSP, it's useful to observe what would be blocked.
Use:
Content-Security-Policy-Report-Only
instead of
Content-Security-Policy
Example:
app.Use(async (context, next) =>
{
context.Response.Headers.Append(
"Content-Security-Policy-Report-Only",
"default-src 'self'; script-src 'self';");
await next();
});
Now resources are not blocked, but violations are reported in the browser console.
This allows you to refine your policy without disrupting users.
CSP for Different ASP.NET Core Applications
Although the CSP header is configured the same way, the policy often differs depending on the application type.
Web APIs
APIs typically return JSON instead of HTML.
A minimal policy is often sufficient:
default-src 'none';
frame-ancestors 'none';
base-uri 'none';
Since no scripts or styles are served, the attack surface is much smaller.
Razor Pages and MVC
Razor applications usually serve HTML, CSS, JavaScript, and images. A policy commonly includes directives such as:
default-src 'self';
script-src 'self';
style-src 'self';
img-src 'self' data:;
font-src 'self';
connect-src 'self';
⚠ Avoid inline scripts and styles whenever possible. If they're required, prefer nonces over 'unsafe-inline'.
Caution
Many developers encounter problems when first adopting CSP.
Some of the most common mistakes include:
Using 'unsafe-inline' for scripts, which weakens one of CSP's primary protections.
Assuming default-src automatically covers every scenario without verifying browser behavior.
A successful CSP policy is usually refined over time rather than written perfectly on the first attempt.
Conclusion
Content Security Policy is one of the most effective browser-based defenses against Cross-Site Scripting. Rather than trying to identify malicious code, it establishes a clear set of rules that defines which resources the browser is allowed to load and execute.
In ASP.NET Core, implementing CSP can be as simple as adding a response header through middleware. However, creating a robust policy requires understanding your application's resource requirements, and continuously refining the policy as the application changes.
In the next article, we'll explore one of the most frequently misunderstood aspects of CSP: nonces, hashes, and why 'unsafe-inline' should almost always be avoided.