/hub
with Apache Basic Auth./user
and websocket routes open. <Location />
) to the entire JupyterHub site, you will break:
Protect only /hub
routes with Basic Auth.
Let all /user
and API routes pass through without additional authentication.
<VirtualHost *:443>
ServerName your-domain.example.com
SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/your-domain.example.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/your-domain.example.com/privkey.pem
Include /etc/letsencrypt/options-ssl-apache.conf
# Protect only the Hub (login, control panel) routes
<Location /hub>
AuthType Basic
AuthName "Workshop Access"
AuthUserFile /etc/apache2/.htpasswd
<RequireAny>
Require user hub_student
Require ip 172.17.0.0/16 # (optional: allow backend/Docker traffic)
</RequireAny>
</Location>
# Allow user servers (JupyterLab/Notebook) to function freely
<Location /user>
Require all granted
</Location>
ProxyPreserveHost On
RequestHeader set X-Forwarded-Proto "https"
RequestHeader set X-Forwarded-Port "443"
RewriteEngine On
RewriteCond %{HTTP:Upgrade} =websocket [NC]
RewriteRule /(.*) ws://YOUR_INTERNAL_JHUB_IP:8000/$1 [P,L]
ProxyPass / http://YOUR_INTERNAL_JHUB_IP:8000/ retry=0
ProxyPassReverse / http://YOUR_INTERNAL_JHUB_IP:8000/
</VirtualHost>
/hub
is the main entry point: Basic Auth here stops bots, brute-force attempts, and unwanted users at the door./user
is for active user sessions: Must be open for JupyterLab, kernels, and websockets to function. JupyterHub manages its own user sessions/tokens for this.fail2ban
) on /hub/login
for extra brute-force protection.