Running JupyterLab Behind an Apache Reverse Proxy

27 Mar 2025 - tsp
Last update 27 Mar 2025
Reading time 2 mins

Introduction

When exposing a JupyterLab server to the internet, especially one running on an internal machine, it’s important to do so securely and robustly. This quick guide covers how to place JupyterLab behind an Apache 2.4 reverse proxy with HTTPS termination, using JupyterLab 4.2.5 running on an internal host. We walk through the necessary Apache configuration, address common websocket issues, and explain what must be configured on the JupyterLab side to ensure proper operation.

In our setup, JupyterLab runs on an internal machine with IP 192.0.2.100 on port 8888. Apache httpd acts as the HTTPS-facing reverse proxy and is accessible to the public via jupyter.example.com.

Apache Configuration

The Apache VirtualHost configuration enables both standard HTTP proxying and websocket tunneling. Websockets are required for many interactive features in JupyterLab, including launching kernels and receiving output. Misconfigured websocket proxying is a common cause of broken kernel behavior, such as failures to start or cells not producing output.

Here’s the relevant Apache 2.4 configuration:

<VirtualHost *:443>
        ServerName jupyter.example.com
        ServerAdmin webmaster@example.com
        DocumentRoot /usr/www/jupyter.example.com/www

        SSLOptions +StdEnvVars
        SSLVerifyClient optional
        SSLVerifyDepth 5
        SSLCertificateFile "/usr/www/jupyter.example.com/conf/ssl.cert"
        SSLCertificateKeyFile "/usr/www/jupyter.example.com/conf/ssl.key"
        SSLCertificateChainFile "/usr/www/jupyter.example.com/conf/ssl.cert"
        SSLCACertificateFile "/usr/local/etc/ssl/ca01_01.cert"

        RewriteEngine On
        RewriteCond %{HTTP:Connection} Upgrade [NC]
        RewriteCond %{HTTP:Upgrade} websocket [NC]
        RewriteRule /(.*) ws://192.0.2.100:8888/$1 [P,L]

        ProxyRequests Off
        ProxyVia Off
        ProxyPreserveHost On
        RequestHeader set "X-Forwarded-Proto" expr=%{REQUEST_SCHEME}

        ProxyPass / http://192.0.2.100:8888/ nocanon
        ProxyPassReverse / http://192.0.2.100:8888/

        RequestHeader edit Origin ^(.*)$ $1
        RequestHeader edit Referer ^(.*)$ $1

        Header unset Content-security-policy
        Header unset Content-security-policy-report-only

        Header always set Access-Control-Allow-Origin "*"
</VirtualHost>

This setup forwards normal HTTP requests and websocket upgrade requests to the internal JupyterLab server. The key parts are the RewriteCond rules and RewriteRule, which ensure websocket connections are passed properly. The ProxyPreserveHost and X-Forwarded-Proto header help the internal server understand the original request’s context.

JupyterLab Configuration

On the internal host running JupyterLab, you must configure it to listen on all interfaces (0.0.0.0), accept connections from the proxy host, and allow remote access. This is done in your ~/.jupyter/jupyter_lab_config.py file:

c.ServerApp.port = 8888
c.ServerApp.local_hostnames = ['jupyter.example.com']
c.ServerApp.ip = '0.0.0.0'
c.ServerApp.allow_remote_access = True
c.ServerApp.allow_origin = '*'

Setting the port to 8888 matches what Apache forwards to. Binding to 0.0.0.0 ensures the JupyterLab server accepts connections from any interface, including from Apache on the same or a different host. The local_hostnames setting tells JupyterLab which hostnames are allowed to appear in requests; this must include the public domain name used by clients. allow_remote_access is necessary to let the proxy connect from another machine, and allow_origin = '*' ensures CORS doesn’t block legitimate browser requests that may originate from outside the internal network.

Conclusion

Running JupyterLab behind an Apache reverse proxy is a practical way to provide secure, HTTPS access to a notebook server running on an internal machine. The main pitfall is usually related to websocket support; getting the rewrite and proxy rules correct is critical for kernel functionality. With the configurations above, JupyterLab should be accessible from the outside while maintaining flexibility and a reasonable level of security.

This article is tagged:


Data protection policy

Dipl.-Ing. Thomas Spielauer, Wien (webcomplains389t48957@tspi.at)

This webpage is also available via TOR at http://rh6v563nt2dnxd5h2vhhqkudmyvjaevgiv77c62xflas52d5omtkxuid.onion/

Valid HTML 4.01 Strict Powered by FreeBSD IPv6 support