Skip to content

Helmet Content Security Policy Global Path not working

I am using Helmet to set up the content security policies of my web app in the backend using Express. The policies look like the following:

const express = require("express");
const app = express();
const helmet = require('helmet');
    directives: {
      defaultSrc: ["'self'"],
      scriptSrc: ["'self'", ""],
      imgSrc: [""],
      objectSrc: ["'none'"],
      styleSrc: ["'self'", "", ""],
      upgradeInsecureRequests: [],

When my app tries to access a link such as It says that it violates the styleSrc policy. But I have specified as one of the policies that is allowed, I thought with that would be accepted as well, since it is a child src. But apparently it gets blocked. How do I allow the child src to pass then? I have tried* but it is invalid.


Author of Helmet here.

Try adding a trailing slash, like this:

This is because /bootstrap doesn’t let you do things like /bootstrap/3.4.0/css/bootstrap.min.css, but /bootstrap/ does. This is a Content Security Policy thing, not a Helmet thing.

For the nitty-gritty details, see step 11 of the “Matching Source Expressions” section in the CSP spec:

If the source expression contains a non-empty path-part, and the URL is not the result of a redirect, then:

  1. Let exact-match be true if the final character of path-part is not the U+002F SOLIDUS character (/), and false otherwise.
  2. Let source-expression-path-list be the result of splitting path-part on the U+002F SOLIDUS character (/).
  3. If source-expression-path-list’s length is greater than url-path-list’s length, return does not match.
  4. For each entry in source-expression-path-list:
    1. Percent decode entry.
    2. Percent decode the first item in url-path-list.
    3. If entry is not an ASCII case-insensitive match for the first item in url-path-list, return does not match.
    4. Pop the first item in url-path-list off the list.
  5. If exact-match is true, and url-path-list is not empty, return does not match.

As an aside, you might want to clean up your Helmet code to something like this:

  contentSecurityPolicy: {
    directives: {
      // ...

Your code uses helmet(), which includes some default CSP middleware, and then later overrides it with helmet.contentSecurityPolicy(). Not a huge deal, but slightly more correct to only use it once.