How to improve security in nginx bloc for jupyterhub

Hi everyone,

I have set up a jupyterhub server with nginx as reverse proxy. I start making the nginx bloc configuration file using the one give in the official documentation and I add some rules to « harding nginx ».

This is the current configuration file :

# Top-level HTTP config for WebSocket headers
# If Upgrade is defined, Connection = upgrade
# If Upgrade is empty, Connection = close
map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;

# HTTP server to redirect all 80 traffic to SSL/HTTPS
server {
    listen 80;

    # Redirect the request to HTTPS
    return 302 https://$host$request_uri;

# HTTPS server to handle JupyterHub
server {
    listen 443 ssl http2;


    ssl_certificate /some/secure/path/jpthb_ada-lovelace_net.cer;
    ssl_certificate_key /some/secure/path/jpthb_ada-lovelace_net.key;

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers on;
    ssl_dhparam /some/secure/path/dhparam.pem;
    ssl_session_timeout 1d;
    ssl_session_cache shared:SSL:50m;
    ssl_stapling on;
    ssl_stapling_verify on;
    add_header Strict-Transport-Security max-age=15768000;
    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-Content-Type-Options "nosniff";
    add_header X-Xss-Protection "1; mode=block";
    autoindex off;
    proxy_hide_header X-Powered-By;
    proxy_hide_header Composed-By;

    # Managing hidden files
    location ~ /\.git {
        return 404;
    location ~ /\~ {
        return 404;
    location ~ /\.back {
        return 404;
    location ~ /\.save {
        return 404;

    # Managing literal requests to the JupyterHub frontend
    location / {
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

        # websocket headers
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        proxy_set_header X-Scheme $scheme;

        proxy_buffering off;
    if ($server_protocol = HTTP/1.0) {
    return 444;


To complete this file, I just need to adapt one rule :

if ($request_method !~ ^(GET|HEAD|POST|OPTIONS)$) {
    return 405;

This one is not sufficient because when I add this in the nginx configuration file, all the users can authenticate themself on Jupyterhub but it’s impossible to create a notebook or to launch a kernel : it always leads to « Errror 405 ». If I remove this rule, Jupyterhub works fine again.

I know I have to add some http protocol in this list GET|HEAD|POST|OPTIONS but I really don’t know wich one.

Could someone see what protocol I miss ?

Thank’s a lot,

PS : all the rules I add come from an internal document of the french Inrae Institution call « Hardening nginx », this document is not specific to jupyterhub configuration.

Try going to your browser console and looking at the network requests:

You can also edit your nginx log format to include the request method, though I’m surprised it’s not already there:

At first glance it looks like PATCH may be needed, but the combination of your browser and nginx logs should confirm this.

Many thanks manics. I try to add PATCH method in the white list but apparently, it’s not sufficient. The notebook is launching throw jupyterlab and it’s possible to use the kernel (it was not possible before adding the PATCH method in the white list) but it’s not stable and if I wait a few minute I get the « error 405 » and « Notebook is unreachable » messages.
So, for now, I forget this rule because I must launch the server. Scientist can’t wait anymore :wink:
As we say in France : « Le mieux est l’ennemi du bien. » :wink:

For the log part, the definitive administrator of the jupyterhub server I set don’t want to use the log, so…

Thanks anyway,

1 Like