Content Security Policy (CSP) is an extra layer of security against attacks such as cross-site scripting (XSS) and data injection. Using CSP, you can specify trusted sources of scripts or media on your website, preventing the browser from loading content from other sources. You can also use CSP to disable the execution of inline CSS and JavaScript.
The value of CSP is commonly set using an HTTP header. However, you can also define CSP using an HTML meta tag. In this post, you’ll learn about Angular Content Security Policy and how to enable it.
What Is Content Security Policy (CSP)?
A CSP is a security feature that makes your site less vulnerable to attacks like XSS. You can use this feature to specify whether your site should allow inline JavaScript or not. In addition, you can specify policies for other content like AJAX, CSS, and iframe. A CSP is sent to the browser using a Content-Security-Policy HTTP header. That is to say, Content-Security-Policy is the key while the actual policy is the value, which we will cover in detail in the next section. The following code shows the format of the Content Security Policy:
Content-Security-Policy: policy
Now let’s take a look at the format of a policy. The policy contains the actual rules for the Content Security Policy. In CSP, a semicolon separates policies for multiple resource types or policy areas. For example, the code below shows a Content Security Policy for multiple resource types:
Content-Security-Policy: default-src 'self'; img-src https:; child-src 'none';
The default-src directive provides a fallback policy for other resource types where they lack their own policy. As a result, you should always include the default-src directive in your Content Security Policy. The value ‘self’ means your website will only load resources from the same origin. For the img-src directive, on the other hand, its value https: means the website only loads images that use the secure HTTPS protocol.
Let’s take a look at another example. This time, you’ll see how to define multiple origins for a directive:
Content-Security-Policy: default-src 'self'; img-src 'self' static.mycdn.com;
What’s new here is that the policy now supports loading images from the same origin and static.mycdn.com. You can add multiple origins by separating each with a space.
Common CSP Directives
CSP uses directives to control different types of content that can be loaded on your page. Each directive serves a specific purpose and can have multiple values that define trusted sources. Here’s a reference to the most commonly used directives:
Directive | Purpose | Common Values |
default-src | Fallback for all resource types | ‘self’ |
script-src | Controls where scripts can load from | ‘self’, ‘unsafe-inline’, nonces |
style-src | Controls CSS sources | ‘self’, ‘unsafe-inline’ |
img-src | Controls image loading | ‘self’, https:, data: |
font-src | Controls web fonts | ‘self’, https://fonts.gstatic.com |
connect-src | Controls AJAX/WebSocket endpoints | ‘self’, API URLs |
Here’s an example of what happens when CSP blocks content. If you try to load an image from wikimedia.com on a page with a restrictive CSP policy, you’ll see an error like this in the browser console:
Content Security Policy: The page's settings blocked the loading of a resource
at https://upload.wikimedia.org/example.jpg because it violates the following
directive: "img-src 'self'"
This error message indicates that the image couldn’t be loaded because the CSP policy only allows images from the same origin (‘self’), but the image was being loaded from an external domain. The browser blocked the request and logged this violation. You can see similar violations in the network tab of DevTools in Chrome, where blocked requests will appear with CSP-related error messages.
How to Enable Content Security Policy in Angular
You can enable a CSP in several different ways in an Angular app. The first is to add the headers directly to the response. The second is to add meta tags to the content. The third is to use Angular’s built-in CSP features (available in Angular 17+). Note that meta tags aren’t supported for some security headers, such as HSTS.
Always prefer setting the CSP via HTTP response headers. Meta tag-delivered CSPs only work for a subset of directives and are not as secure or comprehensive as those delivered via HTTP headers. Use meta tags only in contexts where headers cannot be set (e.g., static file hosts without access to header configuration).
Let’s explore them, starting with a basic Angular app and ending with options for applying a CSP policy on the server.
Client-Side CSP with Meta Tags
When you need to implement CSP without server-level access, meta tags provide a workable solution. This approach is particularly useful for static hosting environments or when working with legacy deployment pipelines. The meta tag method involves adding CSP directives directly to the HTML of your Angular application. Here is a simple example of how that can be done:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>AngularApp</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
<meta http-equiv="Content-Security-Policy"
content="...policy definition here..." />
</head>
<body>
<app-root></app-root>
</body>
</html>
As you can see above, the meta tag contains the Content Security Policy (CSP) directive. Browsers read and interpret meta tags in the head before processing the body. Still, you may also have content, such as scripts and CSS, that is loaded in the head.
Order is important! To apply the CSP to all scripts loaded initially, place this meta tag before script or style tags. Meta-based CSPs must appear in the <head> before any script/style tag; otherwise, they’re ignored or too late to protect anything. Alternatively, you could place the meta tag after script and style tags to avoid adding complexity to the CSP. Of course, doing so will diminish the effectiveness of the security mechanism.
It is important to note that meta tag-based CSPs are parsed after the HTML begins loading, so they cannot block early inline content or scripts. This makes them unsuitable for strict security contexts.
Angular 17+ Automatic CSP
Angular 17 introduced automatic Content Security Policy support, making CSP implementation much easier. With the new autoCsp setting, Angular can automatically apply CSP rules without manual configuration.To enable automatic CSP in Angular 17+, add “autoCsp”: true to your angular.json file, like so:
{
"projects": {
"your-app": {
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"autoCsp": true,
"outputPath": "dist/your-app",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.app.json"
}
}
}
}
}
}
When autoCsp is enabled, Angular automatically:
- Applies CSP rules to your application
- Blocks inline scripts and dangerous methods like eval()
- Ensures only trusted content sources are allowed
Now, if youโre using an older version of Angular and plan to upgrade to Angular 17+, you should avoid inline scripts and replace dangerous methods like eval() and innerHTML with alternative solutions.
Angular Universal (Server-Side Rendering)
For Angular applications using Angular Universal for server-side rendering, you can set CSP headers directly in your server configuration. This approach provides the most security and flexibility.
Here’s an example using Express.js:
// server.ts
import express from 'express';
import { ngExpressEngine } from '@nguniversal/express-engine';
import { APP_BASE_HREF } from '@angular/common';
import { existsSync } from 'fs';
const app = express();
const DIST_FOLDER = join(process.cwd(), 'dist');
// CSP middleware
app.use((req, res, next) => {
const nonce = crypto.randomBytes(16).toString('base64');
const cspPolicy = `
default-src 'self';
script-src 'self' 'nonce-${nonce}';
style-src 'self' 'unsafe-inline';
img-src 'self' data: https:;
font-src 'self';
object-src 'none';
base-uri 'self';
form-action 'self';
frame-ancestors 'none';
upgrade-insecure-requests;
`.replace(/\s{2,}/g, ' ').trim();
res.setHeader('Content-Security-Policy', cspPolicy);
res.locals.nonce = nonce;
next();
});
// Angular Universal engine
app.engine('html', ngExpressEngine({
bootstrap: AppServerModule,
}));
Note that Angular Universal runs only on Node.js and allows you to modify response headers before serving content. You must ensure your Angular components can access the nonce value for any inline scripts.
Web Server
You can also set your CSP headers via the web server. This won’t always be an option and may not even be the best option, but it’s possible. For example, an NGINX server can set response headers at the HTTP, server, and location levels. This provides you with some flexibility to establish distinct policies for various contexts. It does not, however, make it easy to alter the policy along with the code.
Why this may not be the best option:
- Coupling issues: CSP requirements often change with code changes (new third-party scripts, APIs, etc.), but web server configs are deployed separately
- Development friction: If you add a new script source, you need to coordinate both code deployment AND infrastructure changes
- Environment inconsistency: Different environments (dev/staging/prod) might have different web server setups
- Maintenance overhead: Requires infrastructure team involvement for what should be application-level security decisions
Let’s say, for example, that you added a new script source from a third-party provider. In that case, you would also need to update your CSP to allow the script to load. To do that, you’d have to update the NGINX configuration, deploy it, and restart the NGINX process. Going through those steps might not be the best option for you. Still, it’s good to know the option exists.
Here’s an example NGINX configuration:
location / {
add_header Content-Security-Policy "
default-src 'self';
script-src 'self';
style-src 'self' 'unsafe-inline';
img-src 'self' data: https:;
font-src 'self';
object-src 'none';
base-uri 'self';
form-action 'self';
frame-ancestors 'none';
" always;
try_files $uri $uri/ /index.html;
}
The Gotchas of CSP in Angular
CSP and Inline Scripts in Angular
One of the biggest challenges when implementing CSP in Angular applications is handling inline scripts. By default, CSP blocks all inline JavaScript for security reasons; however, Angular apps can generate inline scripts during the build process or at runtime, particularly in development mode.
When you enable a strict CSP, you might see errors like:
Refused to execute inline script because it violates the following Content Security Policy directive: "script-src 'self'"
You have several options to resolve this, each with different security trade-offs:
- Allow all inline scripts – Add ‘unsafe-inline’ to your script-src directive (least secure)
- Use nonces – Generate unique tokens for each inline script (more secure, but complex)
- Use hashes – Create SHA hashes for specific inline scripts (secure, but requires build-time generation)
- Use AOT compilation – Angular’s Ahead-of-Time compilation eliminates most runtime script generation (most secure)
From a practical standpoint, using AOT compilation (which is the default in production builds) is the most secure approach. However, during development, some teams temporarily allow ‘unsafe-inline’ before transitioning to nonces or hashes.
Angular Development vs Production
Development servers often require more permissive policies due to the use of hot reloading and development tools. Although it may be used sparingly in development by some teams, it is critical to never use ‘unsafe-eval’ in production. Here is an example of what the different configurations may look like between environments.
// Development CSP (permissive for tooling)
const devCSP = `
default-src 'self';
script-src 'self' 'unsafe-eval' 'unsafe-inline';
style-src 'self' 'unsafe-inline';
connect-src 'self' ws: wss:;
`
// Production CSP (strict and secure)
const prodCSP = `
default-src 'self';
script-src 'self';
style-src 'self' 'unsafe-inline';
connect-src 'self';
AOT vs JIT Compilation
Angular’s Just-in-Time (JIT) compiler, used primarily in development, requires ‘unsafe-eval’ because it generates code dynamically at runtime. The Ahead-of-Time (AOT) compiler, used in production builds, pre-compiles templates and eliminates the need for ‘unsafe-eval’.
Third-Party Libraries
Many Angular applications use third-party libraries that may inject inline styles or scripts. Popular UI libraries, such as Angular Material, generally work well with CSP, but some may require specific allowances.
Angular 17+ Considerations
If you’re using Angular 17+’s automatic CSP features, be aware that:
- Inline event handlers (like onclick=”…”) will be blocked
- Direct use of eval() or innerHTML will be blocked
- You should migrate to Angular’s event binding syntax: (click)=”method()”
Security Considerations
Let’s walk through a couple of possibilities of how a CSP might be circumvented:
- A script that loads before the meta tag might insert unwanted content before a CSP meta tag.
- A third party between the user agent and the server may be able to add or remove tags or headers before delivering the response.
- Overly permissive policies may leave the page vulnerable to nefarious content.
Additionally, Google Research published a document in 2016 outlining concerns with CSP. Their research indicated that over 99 percent of web pages that used a CSP were still vulnerable to cross-site scripting (XSS) by other means of circumvention. While older studies found that many CSP implementations were ineffective due to overly broad policies, modern tooling and stricter defaults (like Angular’s autoCsp) have made it easier to adopt effective policies today.
Fixing Content Security Policy Errors
In this section, we’ll show you how to detect and fix Content Security Policy errors using a practical example. We’ll add a restrictive CSP policy to an existing Angular application and then work through the common errors you’re likely to encounter.For this example, let’s assume you have an Angular application and want to add CSP protection. Open your src/index.html file and add the following meta tag to the <head> section:
<meta http-equiv="Content-Security-Policy"
content="default-src 'self'; img-src 'self' https: data:;">
This policy is intentionally restrictive, as it only allows resources from the same origin by default and only permits images from HTTPS sources. After adding this CSP policy and running your application (ng serve), you’ll likely see broken functionality.
As you can see in the image above, the page appears broken. The Angular logo isn’t loading, and the page styling (CSS) isn’t being applied properly. This is exactly what we expect when implementing a restrictive CSPโthe browser is blocking resources that don’t meet our security policy.
Debugging CSP Violations
Now let’s diagnose what’s causing these issues. Right-click anywhere on the web page and select “Inspect” (or press F12) to open the browser developer tools. Navigate to the Console tab, where you should see CSP violation reports similar to this:
From the console output shown above, we can see exactly why our images aren’t loading and why our inline CSS isn’t working. Each error message clearly identifies which CSP directive is being violated and what resource was blocked.
The first error reads:
Refused to load the image 'http://localhost:4200/favicon.ico' because it violates the following Content Security Policy directive: "img-src https://*"
This error occurs because our CSP policy (img-src https://*) only allows images from HTTPS sources, but the favicon is being served from the local development server using HTTP. One way to resolve this error is to allow non-HTTPS images from the same origin by including ‘self’ as an allowed source for the img-src directive.
The next error outputs the following message:
Refused to apply inline style because it violates the following Content Security Policy directive: "default-src 'self'".
To fix this, we may need to add ‘unsafe-inline’. However, you should avoid allowing inline scripts on your site unless it is necessary.
Now we are left with one final error, which outputs the following message:
Refused to load the image 'data:image/svg+xml;base64,PHN2... QwLjl6IiAvPgogIDwvc3ZnPg==' because it violates the following Content Security Policy directive: "img-src https://* 'self'".
This error, like the first one, affects image loading. Notice that this image is not from an HTTPS source, nor is it on the origin file system. In order to fix this, we include data: to the img-src directive. After addressing all these CSP violations, your Angular application should load properly, with all images and styles functioning correctly. Here’s what the page looks like after fixing the CSP policy:
Perfect! Notice that the browser console is now clear of CSP violation errors. The Angular logo loads correctly, the styling is applied, and the application functions as expected.
Here is the complete code for the new Content Security Policy that doesn’t break our images or CSS:
<meta http-equiv="Content-Security-Policy"
content="default-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' https: data:;">
Note that the ‘unsafe-inline’ keyword should only be used in specific directives like script-src and style-src. Never use ‘unsafe-inline’ in default-src as it serves as a fallback for both script and style directives, making your entire policy vulnerable. In this example, we’ve correctly placed it under style-src to allow Angular’s inline styles while keeping the policy secure.
Testing and Debugging
Modern browsers provide excellent tools for debugging CSP:
- Console: CSP violations appear in the browser console
- Security Tab: Chrome DevTools shows CSP status
- Network Tab: Blocked requests show CSP violation details
You can also use online tools:
- CSP Evaluator (https://csp-evaluator.withgoogle.com/) – Google’s CSP analysis tool
- Mozilla Observatory for comprehensive security testing
- Report URI – CSP monitoring and reporting service
Validating Your Angular CSP Configuration with StackHawk
Instead of waiting for your security team to discover these issues later in the development lifecycle or when they hit production, let’s use StackHawk to automatically identify potential vulnerabilities for us. As a modern DAST platform, StackHawk scans your running application and tests it for issues, such as CSP misconfiguration, XSS, and other critical vulnerabilities in the OWASP Top 10.
To do this, you’ll need to ensure you have a StackHawk account. If you need one, you can sign up for a trial account or log into an existing account.
If you’re logging into an existing StackHawk account, from the Applications screen, you’ll click Add Application.
If you’re new to StackHawk, you’ll be automatically brought into the Add an App flow. On the Scanner screen, you’ll see the instructions for installing the StackHawk CLI. Since we will be running our testing locally, we will use this option. Once the hawk init command is executed successfully, click the Next button.
On the next screen, you will fill out an Application Name, Environment, and URL. Once filled out, click Next.
Since we will be testing an Angular application with web pages, on the next page, we will choose our Application Type as “Dynamic Web Application/Single Page Application”.
Depending on your Angular app setup, you’ll also configure the application details. For example, if you have API endpoints in your Angular app, you would set the API Type to “REST / OpenAPI” and point to your OpenAPI specification file by selecting URL Path and adding the path to where your OpenAPI spec is located. Alternatively, you can click “Skip for now” if you’re unsure. Once complete, click Create App.
Lastly, we will need to add a stackhawk.yml file to the root of our Angular project. Once the file is added, copy the screen’s contents, paste them into the file, and save it. Lastly, we can click the Finish button.
In our root directory, you should see the stackhawk.yml file we’ve added:
Run HawkScan
Now weโre ready to test our Angular application. In a terminal pointing to the root of our project, we will run HawkScan using the following command:
hawk scan
After running the command, the tests will execute in the terminal.
Note: If you get an error similar to the one below, this means that your Angular application is not running in HTTPS, and that is how HawkScan is trying to call the app:
HawkScan Target Not Found Error: Unable to access https://localhost:3000. Check if the web server is listening on the specified port.
To fix this, either add HTTPS capabilities to your Angular app or, more simply, change the host entry in your stackhawk.yml to use “http”.
This will run tests against your Angular application and its pages. Once the tests have run, we can begin to explore any findings that were discovered.
Explore The Initial Findings
Once the tests are complete, the terminal will contain some information about any vulnerabilitiesโincluding CSP vulnerabilitiesโfound. In the screenshot below, you can see that StackHawk has identified CSP vulnerabilities across multiple pages within the Angular application.
To explore the vulnerabilities further, we will click on the test link at the bottom of the output. This will take us into the StackHawk platform to explore further.
After clicking on the link, we can now view the test results in a nicely formatted display. This helps us confirm that the CSP issue found was, in fact, fixed by our updated implementation. StackHawk helps us to confirm this after the rescan. Next, we will click on the CSP Misconfiguration entry.
Within this entry, we can see an Overview and Resources that can help us with fixing this vulnerability, as well as the Request and Response that the application returned on the right side of the screen. Above this, you will also see a Validate button, which will display a cURL command with the exact HTTP request used to expose the vulnerability.
Understand and Fix the Angular CSP Security Issue
When StackHawk identifies CSP vulnerabilities, you’ll see detailed findings that indicate which paths are affected and provide potential remediation techniques (as shown in the image above). Using the techniques in this guide or the remediation advice provided by StackHawk for the vulnerability, you can then make the necessary adjustments to your code and configuration. Once fixed, ensure that you stop your web servers and redeploy the latest code.
Confirm the Fix!
With the latest configuration deployed, we can use StackHawk to confirm that the fix implemented actually resolves our CSP misconfiguration issues and is secure. To do this, we will click the “Rescan Findings” button in StackHawk.
Then, we will see a modal containing the “hawk rescan” command that includes the correct Scan ID. You’ll run this command in the same terminal where you ran the initial set of tests.
In the output, you will again see any vulnerabilities that were found during the scan. In this case, you’ll see that the CSP misconfiguration vulnerabilities are no longer showing. Clicking on the link at the bottom of the terminal output, you can confirm that the CSP misconfiguration vulnerabilities have now been added to the Fixed Findings from Previous Scan, confirming that the vulnerability has been successfully fixed and has passed any vulnerability tests.
With that, we’ve successfully remedied and retested our Angular application to ensure its safety from potential CSP misconfiguration attacks. With StackHawk, this can be done on a local machine (as demonstrated here) or automatically as part of a CI/CD flow. Regardless of how itโs used, StackHawk provides developers with direct insight into the vulnerabilities present within their code and the tools to fix and validate those fixes.
Final Thoughts
Content Security Policy is a great security feature in modern web browsers. Using CSP and following other security best practices can eliminate most XSS-related attacks from your website. In this post, we showed you what Content Security Policy is and how it applies to Angular applications. We also showed you how to enable it. Additionally, we practiced detecting and resolving Angular Content Security Policy errors.
A good place to start when debugging Angular Content Security Policy errors is by using the browser console. The console feature is available by default in most modern web browsers, including Chrome and Firefox.
And finally, ensure that you fix Angular Content Security Policy errors by changing the policies to new values that work and remain secure. If youโd like to see how CSP works in other JavaScript frameworks, check out our other guides:
Remember that security is an ongoing process. As your application grows and you add new dependencies, regularly review and update your CSP to ensure it continues to provide effective protection without breaking functionality.
Want to ensure your Angular CSP configuration is secure? Sign up for StackHawk today for a free 14-day trial to automatically test your CSP configuration and detect potential vulnerabilities