How to dynamically set a CSP nonce value to a Content-Security-Policy header in the header directive

1. The problem I’m having:

I’m setting up Content-Security-Policy in my Caddyfile. In my system, Caddy works as a reverse proxy to my Django application. I’d like to set the header in the Caddy server to cover the reverse-proxied requests to Django and direct requests to static content served by Caddy.

I need to do the following things:

  1. Generate a random string in Caddy during each request
  2. Send the generated random string down to Django with header_up to use in Django templates
  3. Use the random string in the header directive to always include the nonce-xxx attribute in Content-Security-Policy headers.

The problem is that I haven’t found a single way to do this in the Caddyfile. It seems like this can be done easily in Caddy templates with Go’s standard library but that doesn’t suit me because I’m using Django to generate and render the response bodies.

Here’s what I found about the templates:

Does somebody have information if the nonce value generation is possible with Caddy without external plugins?

One option is to use luludotdev/caddy-requestid like this:

    request_id {
	nonce 24
    }

    header {
        Content-Security-Policy "script-src-elem 'self' nonce-{http.request_id.nonce}"
    }

    reverse_proxy localhost:8081 {
        header_up X-CSP-Nonce {http.request_id.nonce}
    }

But I would prefer a built-in solution a LOT more so that I don’t have to convert into using a custom Caddy build. That brings another unwanted things as well.

3. Caddy version:

Here’s my local test version:

./caddy version
v2.8.4

4. How I installed and ran Caddy:

go install github.com/caddyserver/xcaddy/cmd/xcaddy@master
xcaddy build --with github.com/luludotdev/caddy-requestid@e72ff6fdd2edf9c31bf9f006930ab227779edbd7
./caddy run --config Caddyfile adapter caddyfile

a. System environment:

  • Ubuntu 24.04
  • Go 1.22.5

You can do that with the {uuid} placeholder, built-in.

3 Likes

Thanks for the reply. What we ended up doing, was not using Caddy at all for the nonce generation and Django will handle all of the CSP handling.

Although, using {uuid} would probably be the best option if I need to generate it on the web server and not in a backend. At first, it felt dirty to include a request identifying UUID sring as the nonce but I already got over the feeling. :smile:

That’s more correct anyway (CORS is an application-layer concern) so :+1:

1 Like