Custom CSP for Payload in Express server with frontend app

default discord avatar
58bits
2 weeks ago

Posting here in case anyone else comes across this. We have a custom express server serving both Payload and our frontend app. Payload admin is available via

/admin

The frontend app and Payload have quite different CSP settings. If you're using

https://helmetjs.github.io/

- then you can chain helmet middleware and return different policies depending on the request path. For example...



// CSP policy via helmet
app.use(function (req, res, next) {
  let middleware
  // If we've mounted Payload CMS on /admin - change the CSP to suit.
  if (req.path.startsWith('/admin')) {
    middleware = helmet({
      contentSecurityPolicy: {
        'script-src': [
            "'self'"
          ],
          'img-src': ["'self'", 'data:', 'cdn.yourcdn.com'],
          'media-src': ["'self'", 'data:', 'cdn.yourcdn.com'],
          'default-src': ["'self'"]
      }
    })
  } else {
    middleware = helmet({
      crossOriginEmbedderPolicy: false,
      contentSecurityPolicy: {
        // NOTE: Remove reportOnly when you're ready to enforce this CSP
        // reportOnly: true,
        directives: {
          'connect-src': [
            ENVIRONMENT === 'development' ? 'ws:' : null,
            "'self'",
          ].filter(Boolean),
          'script-src': [
            "'strict-dynamic'",
            "'self'", // Ignored by CSP 3 compliant browsers when strict-dynamic - here for backwards compat.
            "'unsafe-inline'", // Ignored by CSP 3 compliant browsers when strict-dynamic - here for backwards compat.
            'https:', // Ignored by CSP 3 compliant browsers when strict-dynamic - here for backwards compat.
            'http:', // Ignored by CSP 3 compliant browsers when strict-dynamic - here for backwards compat.
            (_, res) => `'nonce-${res.locals.cspNonce}'`
          ],
          'script-src-attr': [
            (_, res) => `'nonce-${res.locals.cspNonce}'`
          ],
          'font-src': ["'self'"],
          'frame-src': ["'self'"],
          'img-src': ["'self'", 'data:', 'cdn.yourcdn.com'],
          'media-src': ["'self'", 'data:', 'cdn.yourcdn.com'],
          'default-src': ["'self'"],
          'upgrade-insecure-requests': null
        }
      }
    })
  }

  middleware(req, res, next)
})

Hope this helps ;-)

    Open the post
    Continue the discussion in Discord
    Like what we're doing?
    Star us on GitHub!

    Star

    Connect with the Payload Community on Discord

    Discord

    online

    Can't find what you're looking for?

    Get help straight from the Payload team with an Enterprise License.