CSP Issue with Grafana: Setting Nonce Dynamically for Apache Proxy

I’m trying to set up a strict Content Security Policy (CSP) for Grafana when it’s proxied through Apache. CSP requires that all inline scripts include a nonce. I have successfully configured Apache to generate a unique nonce using mod_unique_id and pass it as a custom HTTP header (X-Nonce) in the response.

However, I’m facing the following issues:

  1. Grafana includes <script nonce=""> in the HTML, but the value is not dynamically replaced with the nonce from the proxy.
  2. Attempts to use Apache’s mod_rewrite, mod_substitute, or mod_sed to inject the nonce have failed.

Steps Taken:

  1. Enabled mod_unique_id and verified that the UNIQUE_ID is being generated successfully.
  2. Configured RewriteRule to set MY_NONCE environment variable:

in apache

RewriteEngine On
RewriteRule .* - [E=MY_NONCE:%{UNIQUE_ID}]
  1. Used Substitute to replace the nonce in the response:

in apache

AddOutputFilterByType SUBSTITUTE text/html
Substitute "s|<script nonce=\"\">|<script nonce=\"%{MY_NONCE}e\">|ni"
  1. Added a custom HTTP header for debugging:

in apache

Header set X-Nonce "%{MY_NONCE}e"

Observed Behavior:

  • The X-Nonce header is correctly set with a unique value.
  • The Substitute directive is not resolving %{MY_NONCE}e, and the resulting HTML contains:

html

<script nonce="%{MY_NONCE}e">

Expected Behavior:

  • The nonce should be dynamically inserted into the HTML:
<script nonce="Z32Fn8lRIGOvwlHGQlsdZAAAAAA">

Questions:

  1. How can I dynamically inject the nonce into Grafana’s <script> tags?
  2. Is there a recommended way to modify Grafana’s behavior to read and use the X-Nonce header value?
  3. Are there known workarounds or plugins to support CSP with Grafana behind an Apache proxy?
2 Likes

Why Apache should set a nonce, when you can do that on the Grafana level?

Namaskaram jangaraj,

We have Apache as proxy for our web application, including Grafana. For the web application, we are making use of UNIQUE_ID generated in Apache to set it as nonce value.

To enable CSP, we have followed Configure security hardening | Grafana documentation
But it didn’t work.

So we thought it’s conflicting with the nonce generated by apache, and trying to the same nonce generated by Apache in Grafana as well.

Thanks for raising that question, will review the configuration one more time.

Thanks,
Chakradhar.

Namaskaram jangaraj,

Thank you very much for responding to my query.

Tried following Configure security hardening | Grafana documentation

Changed only

content_security_policy = true

Restarted Grafana and tried logging into Grafana, but login page didn’t load, because of the CSP issue.

Header has the CSP value, below screenshot:
Screenshot 2025-01-13 121626

But the index.html does not has nonce value, find the below screenshot

Please guide me if there are any other configurations are missing.
Just to let you know, I haven’t modified “content_security_policy_template” it’s the default value.

Thanks,
Chakradhar.

You didn’t provide reproducible example (e.g. which Grafana version you are using).
You modified source code:


You are using Apache rewrites, …
There is no way to help you efficiently with all this “hacks” on your end.

Use the latest stable Grafana version without any modifications and without Apache and prove me that nonce doesn’t work there, pls.

I didn’t modify the source code, if I check the files shipped along with Grafana installer/bundle, came across the below file:
grafana/public/views/index.html, for reference has below code snippet:

Screenshot 2025-01-13 165740
and which I believe, the Grafana runtime is/should replace with nonce id dynamically before rendered to the UI.

<script nonce="[[.Nonce]]">

snippet is there in multiple places on the same file.

Grafana version : v11.2.0

Yup, will setup standalone Grafana and try to reproduce the issue.

Thanks,
Chakradhar.

See source code for 11.2.0:

There is no console.log, but you have that one => you have modified source code of your Grafana.

1 Like

Namaskaram jangaraj,

Sorry for the delayed response, was stuck with other priority issues.

As we have Apache as proxy, for our application and grafana, CSP is set in Apache. Because of this the nonce generated in Apache is not matching with nonce generated in Grafana.

Below are the configurations made in Apache:

uncommented the below line, to generate unique id

LoadModule unique_id_module modules/mod_unique_id.so
<IfModule mod_headers.c>
   RequestHeader set MY-NONCE %{UNIQUE_ID}e
   Header set MY-NONCE %{UNIQUE_ID}e

   Header set Content-Security-Policy "default-src 'self' ; script-src https: 'self' 'nonce-%{UNIQUE_ID}e' ; connect-src 'self' ; img-src data: https: ; style-src 'self' 'unsafe-inline' ; font-src 'self' data: ; base-uri 'self' ; form-action 'self' ; frame-ancestors 'self' ; frame-src 'none' ; child-src 'none' ; object-src 'self'; worker-src 'self' ; navigate-to 'self' ; media-src 'self';"
</IfModule>
ProxyPreserveHost On
ProxyPass "/metricsgraph/" "http://localhost:3000/"
ProxyPassReverse "/metricsgraph/" "http://localhost:3000/"

Changes in Grafana configuration, default.ini

content_security_policy = true
root_url = %(protocol)s://%(domain)s:%(http_port)s/metricsgraph/

Please find the below screenshots, with response on the client-side/browser
Error due to nonce mismatch

nonce set by Apache

nonce set by Grafana
Screenshot 2025-01-22 204240

Since there is a mismatch between the nonce generated by Apache and Grafana, cannot access Grafana.

If we disable the below configuration in Apache

#Header set Content-Security-Policy

able to access Grafana, but cannot access our Application, as nonce is not set.

So we looking for inputs, if there is a mechanism in Grafana which can consume the nonce id generated in Apache, could you please guide me here.

I’m repeating my question:

Why Apache should set a nonce, when you can do that on the Grafana level?

As I mentioned above, that is the current implementation, where our Application needs nonce or CSP enabled, because of which, it’s been set in Apache, so that all modules deployed and proxied through Apache, makes use of the same nonce, not required to be generated at all the modules.

Hope this clarifies,