Correct Content Security Policy for use with Proxy

Hi forum!


I configured a jupyterhub on a VM which worked perfectly until we took some security measures.
The setup is the following:

  • The jupyterhub service is listening on port
  • The security proxy presenting a certificate does proxy_pass from HUB.DOMAIN.TLD:443 to

As soon as the securied setup was activated the Content Security Policy seemed wrong. This is what my browser is showing:
Refused to execute inline script because it violates the following Content Security Policy directive: "script-src 'self'". Either the 'unsafe-inline' keyword, a hash ('sha256-<some hash>'), or a nonce ('nonce-...') is required to enable inline execution

I tried

I am not an expert in this field, but I read myself into the topic and I understand that the default Content Security Policy(CSP) disables execution of inline javascript code, which is used extensively by Jupyter.[1]

Also CSP is used to prevent XSS. [2]

However I tried to alter the CSP settings in /opt/jupyterhub/etc/jupyterhub/ for making it work. Took me a day messing around with c.JupyterHub.tornado_settings no success yet.

I tried is for example

c.JupyterHub.tornado_settings = {'headers': {'Content-Security-Policy': "script-src * data: blob: 'unsafe-inline' 'unsafe-eval';"}}


Plugin '@jupyterlab/filebrowser-extension:share-file' failed to activate.
(anonymous) @ index.es6.js:282
Promise.catch (async)
(anonymous) @ index.es6.js:281
e.start @ index.es6.js:280
o @ index.out.js:1011
load (async)
ANye @ index.out.js:1060
t @ bootstrap:84
0 @ main.71316899e49699071e9d.js:1
t @ bootstrap:84
i @ bootstrap:45
s @ bootstrap:32
(anonymous) @ vendors~main.8a850565bae0c4974bdc.js:2
index.es6.js:283 EvalError: Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of script in the following Content Security Policy directive: "script-src 'self'".

    at new Function (<anonymous>)
    at g.R (index.js:120)
    at g.l (resolve.js:54)
    at Object.j [as resolveRef] (index.js:189)
    at Object.e [as code] (ref.js:21)
    at Object.e [as validate] (validate.js:277)
    at Object.e [as code] (properties.js:201)
    at e (validate.js:374)
    at R (index.js:88)
    at g.f (index.js:55)

I’ve read through all the issues on Github but mainly people have these problems when embedding jupyterhub into an iframe.

I’d appreciate ideas on the topic. I believe my setup is a common one. Probably I am missing something. Let’s find an answer that helps other googling this.

All the best


Still hoping someone may be able to help!

My main problem seems to be that my CSP settings for tornado seem to be ignored.

c.JupyterHub.tornado_settings = {'headers': {'Content-Security-Policy': "script-src 'self' 'unsafe-eval' 'unsafe-inline'; object-src 'self'; style-src 'self' 'unsafe-inline';"}}

I added CSP to the NotebookApp. As I am using JupyterLab and not Jupyter Notebook probably “NotebookApp” is wrong here, right?

c.Spawner.args = ["–NotebookApp.tornado_settings={ ‘headers’:{ ‘Content-Security-Policy’: “script-src ‘self’ ‘unsafe-eval’ ‘unsafe-inline’; object-src ‘self’;style-src ‘self’ ‘unsafe-inline’;”} $

What I find miserable though is the fact that even on the login page the CSP is not the one I set:
connect-src 'self'; default-src 'self'; font-src 'self'; frame-ancestors 'none'; frame-src 'none'; img-src 'self'; script-src 'self'; style-src 'self'