Understanding the Content-Security-Policy header
What is a Content Security Policy (CSP)?
A CSP is an HTTP response header (Content-Security-Policy) sent by a web server. It tells the browser what sources of content are allowed for things like:
- default-src → fallback for everything
- script-src → allowed sources for JS
- style-src → allowed CSS
- img-src → allowed images
- connect-src → allowed URLs for XHR, fetch, WebSockets, etc.
If something is loaded from a source not listed, the browser blocks it before the request even goes out.
For example, if your website is foo.com and you have an image on the page like so:
<img src="http://bar.com/some-pic.jpg">
- If the foo.com server does NOT set a Content-Security-Policy header then the image will be loaded (browsers will only enforce CSP headers if they are set)
- If the foo.com server DOES set a Content-Security-Policy header then the image will not load unless the CSP header allows it like so:
Content-Security-Policy: default-src 'self'; img-src 'self' http://bar.com;
Note that the 'self' allows resources to be loaded from origin domain (which is foo.com in this example)
Also note that you can use the default-src to set defaults for any resources that are not specifically included in the header.
CSPs are like contracts between the site delivering the page and the browser. The browser will only allow resources to be fetched if CSP header allows it. If there is no CSP header then the browser will allow resources to be fecthed unless something else gets in the way (like CORS issues).
It's possible to set content security settings in a web page by adding a meta tag:
<meta http-equiv="Content-Security-Policy" content="default-src 'self'">
But if the server also sets the Content-Security-Policy header, then the browser will ignore the meta tag.
One of my projects included a front end that made API/AJAX calls to a backend. The front end was a React app that was set up on an Apache server. To configure the the CSP header, I created an .htaccess file which had this in it:
Header always set Content-Security-Policy "default-src 'self'; connect-src 'self' https://api.mybackend.com; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self' data:;"
Notice the the connect-src is set to 'self' and to the url of my backend api.
The backend was an Express API and I used the helmet package to set headers. Here's how I configure the helmet middleware:
const helmet = require("helmet");
app.use(
helmet.contentSecurityPolicy({
useDefaults: true,
directives: {
"connect-src": ["'self'", "https://api.mybackend.com"],
},
})
);
This is required if the API server is sending the CSP header (which it does by default when using helmet) because the browser runing the frontend code applies CSP polices for any response that includes a CSP header.
Apparently, I could have also gotten the AJAX calls to work by disabling the CSP header in helmet all together:
app.use(
helmet({
contentSecurityPolicy: false
})
);
This is recommended for APIs that only serve JSON, but if the API server also sent other resources (such as HTML pages, images, etc.) then you should include the CSP header, and configure it carefully.