Reverse proxy
Household addresses must be served over HTTPS by a reverse proxy you control, such as Caddy, nginx, Nginx Proxy Manager or Traefik. JellyPass decides which household a request is for from its Host header, so the proxy's job is simple but strict.
What the proxy must do
- Pass the original
Host, exactly one. JellyPass refuses a request naming more or fewer than one host with400 invalid_host. - Forward WebSocket upgrades and byte-range requests, and avoid buffering large media responses.
- Append to
X-Forwarded-Forrather than pass through whatever the client sent. - Leave
no-transformin place. JellyPass marks every HTML pageCache-Control: …, no-transformand compresses it itself. - Send only household names to JellyPass from the internet. In JellyPass 3.x one port serves everything, and any host name that is not a household address gets the admin site. Don't forward a catch-all wildcard to JellyPass; give the admin site its own name and restrict it (see Security).
DNS: point each household name, or a wildcard record such as *.example.com, at the proxy. Household names are a single label under your domain (jelly-grove.example.com), so one wildcard certificate covers them all; a wildcard certificate usually needs your proxy's DNS challenge.
Client addresses: TRUSTED_PROXIES and Jellyfin's known proxies
Behind a proxy every request reaches JellyPass from the proxy's address. Set TRUSTED_PROXIES to the address (or range) JellyPass sees the proxy connect from, so sign-in rate limits apply per client:
TRUSTED_PROXIES=192.168.1.5
With Docker's published ports this is often the Docker network's gateway address rather than 127.0.0.1; docker network inspect shows it. List only proxies you control: JellyPass walks X-Forwarded-For from right to left, skips trusted proxies and uses the first address it doesn't trust. Requests from addresses not listed are attributed to that address and their header is ignored.
JellyPass forwards to Jellyfin like any proxy hop. For Jellyfin to see real client addresses, add JellyPass and your reverse proxy in Jellyfin under Dashboard → Networking → Known proxies.
With a CDN in front of your proxy, either have the proxy restore the real client address from the CDN's header, or add the CDN's published ranges to both TRUSTED_PROXIES and Jellyfin's known proxies. Otherwise every client looks like a CDN server.
Examples
These are starting points, not complete configurations. Replace example.com, the household names and the LAN range with your own. Both were checked with Caddy 2.10 and nginx 1.28, including that a request naming any other host never reaches JellyPass.
Caddy
Caddy passes Host, handles WebSockets and streams responses by default, and gets certificates itself. Here Caddy shares a Docker network with JellyPass; list each household address:
# Household addresses: list each one.
jelly-grove.example.com, jelly-heath.example.com {
reverse_proxy jellypass:8787
}
# The admin site: its own name, reachable from the LAN only.
jellypass-admin.example.com {
@lan remote_ip 192.168.1.0/24
handle @lan {
reverse_proxy jellypass:8787
}
respond 403
}
nginx
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
# Any other name: refuse it. Without this block the first server below
# would become the default and pass unmatched names to JellyPass, which
# answers any non-household name with the admin site.
server {
listen 80 default_server;
listen 443 ssl default_server;
ssl_reject_handshake on;
return 444;
}
# Household addresses: jelly-<household>.example.com
server {
listen 443 ssl;
server_name ~^jelly-[a-z0-9-]+\.example\.com$;
ssl_certificate /etc/nginx/certs/wildcard.example.com.crt;
ssl_certificate_key /etc/nginx/certs/wildcard.example.com.key;
location / {
proxy_pass http://127.0.0.1:8787;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_buffering off;
proxy_read_timeout 1h;
}
}
# The admin site: its own name, LAN only.
server {
listen 443 ssl;
server_name jellypass-admin.example.com;
ssl_certificate /etc/nginx/certs/wildcard.example.com.crt;
ssl_certificate_key /etc/nginx/certs/wildcard.example.com.key;
allow 192.168.1.0/24;
deny all;
location / {
proxy_pass http://127.0.0.1:8787;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
}
}
If you use a different HOUSEHOLD_HOST_PREFIX, change jelly- in server_name to match. Keep the default_server block: nginx picks the server by the request's Host, not only by the name the TLS connection used, so a request naming any other host is closed instead of reaching the admin site. The Caddy example needs no such block: Caddy serves only the names listed and answers any other Host with an empty response, without passing it to JellyPass.
Nginx Proxy Manager and Traefik
The same rules apply: one proxy host (or router) per household name, or one that matches only your household pattern, pointing at JellyPass on port 8787 with WebSocket support on, and a separate, restricted host for the admin site. Nginx Proxy Manager passes Host unchanged; turn on Websockets Support for household hosts.