JupyterHub is designed as a simple multi user server for modestly sized groups of semi-trusted users. Its documentation says it is not suitable for untrusted users in the default configuration. For a team that knows each other, defaults can be fine. For open enrollment, enable per user subdomains and lock down the environment.
Definition
A semi-trusted user is someone you know and who has a reason to be on the system, such as a colleague or an enrolled student, and who is not expected to attack other users. An untrusted user is anyone you cannot say that about. The JupyterHub security overview draws this line.
Why the default has limits
By default the Hub and every single user server sit on one domain behind a proxy. The documentation explains the consequence: browsers see the whole thing as a single website. Many of the web's cross site protections therefore do not apply between single user servers and the Hub, or between servers.
The risk it names is that a user must never be able to write arbitrary HTML and serve it to another user on the Hub's domain. Authentication handles this by allowing only the owner of a server to view pages that server serves.
What administrators must ensure
To protect users from each other, the documentation says a user must not be able to modify their own single user server. That includes:
- Installing new packages into the Python environment that runs the server.
- Creating files in any PATH directory that comes before the one holding the single user executable, if PATH is used to find it.
- Changing environment variables such as PATH or PYTHONPATH for the server.
- Changing the notebook server configuration directory.
- Choosing an unrestricted base image in container based setups.
Subdomains and sharing
| Setting | What it gives you |
|---|---|
| Per user subdomains | Each user server gets its own domain, so cross origin protections work between servers |
| Sharing off, the default | Users cannot serve each other files by sharing access |
| Sharing on | Not suitable for untrusted users unless per user domains are also enabled |
The documentation calls subdomains the only way to reliably isolate user servers from each other, and says JupyterHub makes no guarantee of protecting users from each other unless they are enabled. It also advises serving the Hub on a domain that is not a subdomain of sensitive content. Subdomains need wildcard DNS and a wildcard certificate, which some institutions cannot get.
A decision guide
- Know your users? Semi-trusted defaults may do, with the lockdown list above.
- Open to the public or a large course? Enable subdomains, or choose a different design.
- Need to share work between users? Turn sharing on only with subdomains.
- Hosting on a company domain? Use a separate domain for user content.
- Plan periodic security reviews. The overview recommends them.
Remember that a notebook server runs code as its user, so isolation of accounts, containers and storage matters as much as the web settings. See /resources/blog/jupyter-execute-permission-means-arbitrary-code-authorizer-explained.
Safeguard's asset discovery can help you find which Jupyter hosts exist across your estate, and dependency scanning covers the images you build for them. Try the free scan at gold.safeguard.sh/scan.
Quick check
Is JupyterHub suitable for untrusted users out of the box?
No. The documentation says it is not suitable for untrusted users in its default configuration.
What is the only reliable way to isolate user servers?
Per user subdomains, according to the documentation.
Sources
- Security Overview, JupyterHub documentation, fetched 19 October 2026.
Last reviewed: 19 October 2026.