I have run Jellyfin at home for years, and I can tell you with absolute certainty: opening port 8096 to the internet is a security nightmare you do not want. Bots hit that port within minutes of going live, your login page ends up in Shodan, and unencrypted traffic means anyone on your coffee-shop Wi-Fi can snoop your library. The clean solution is to put a Jellyfin reverse proxy in front of your server so all remote access flows through HTTPS on ports 80 and 443 while port 8096 stays closed to the outside world.
This guide walks through exactly how I configure Jellyfin remote access behind a reverse proxy with zero open ports to the internet. You will learn the prerequisites, the dashboard settings most people miss, three reverse proxy recipes you can copy-paste, and the no-open-ports alternatives I lean on when the network is unfriendly (CGNAT, double NAT, hotel Wi-Fi). I will also cover the troubleshooting steps that fix the most common Reddit pain points, including the dreaded “apps do not work but the web UI works” loop.
By the end, your Jellyfin server will be reachable from anywhere at a friendly address like jellyfin.example.com, with HTTPS, automatic certificate renewal, and proper brute-force protection. You will not need a single forwarded port other than 80 and 443 on the proxy host, and even those become optional if you go the Tailscale or Cloudflare Tunnel route.
Table of Contents
Quick Summary: Three Ways to Reach Jellyfin Remotely
If you want the shortest possible path to a working setup, here is how I think about the three options. Each one gives you Jellyfin remote access, but they differ sharply in security, performance, and how friendly they are to your family.
Here is a side-by-side breakdown I use when friends ask which to pick.
Reverse proxy with HTTPS (recommended for most people). A small server (SWAG, Nginx Proxy Manager, or Traefik) terminates TLS, hands out Let’s Encrypt certificates automatically, and forwards clean HTTP traffic to Jellyfin on your LAN. Pros: works on every device including smart TVs, fastest streaming, clean domain. Cons: you still open port 443 to the internet, so you need a hardened proxy.
VPN mesh (Tailscale, ZeroTier, WireGuard). Your devices join a private overlay network and Jellyfin listens only on that overlay. Pros: zero ports open, great personal use. Cons: family members need to install a VPN client, smart TVs are awkward, streaming sticks often refuse to install VPN apps.
Cloudflare Tunnel. A small daemon (
cloudflared) opens an outbound tunnel from your Jellyfin host to Cloudflare’s edge. Pros: zero inbound ports, free HTTPS, optional Zero Trust login. Cons: requires a domain on Cloudflare, all traffic routes through Cloudflare’s network, and 4K streaming can be bandwidth-limited.
For a household with smart TVs, Android phones, iPads, and that one cousin who uses a Fire Stick, the reverse proxy method is by far the smoothest. Tailscale wins for personal-only access across laptops and phones. Cloudflare Tunnel is the best fit when you want zero open ports and you are okay pushing traffic through Cloudflare.
The decision matrix below is what I use as a shortcut. Pick reverse proxy when device compatibility matters most. Pick Tailscale when you are the only user. Pick Cloudflare Tunnel when you cannot open any ports at all.
What Is a Reverse Proxy (and Why Jellyfin Needs One)?
A reverse proxy is a small server that sits between the internet and Jellyfin. It terminates TLS (meaning it decrypts HTTPS traffic, handles the certificate, then re-encrypts or sends plain HTTP internally), forwards each request to Jellyfin on port 8096, and sends Jellyfin’s response back out over a secure channel. From the outside world, no one ever talks to Jellyfin directly; they only see the proxy.
Why does this matter? Because Jellyfin by default runs plain HTTP on port 8096. Exposing that port directly does three bad things at once: it puts your login page on the open internet where every Shodan crawler can find it, it transmits credentials in cleartext so anyone on the path can read them, and it leaks your home public IP to anyone who connects. A reverse proxy fixes all three by handling HTTPS, hiding Jellyfin behind it, and giving you a clean domain like jellyfin.example.com.
This is also the architecture that makes the “no open ports” approach possible. If you put the proxy on a VPS or use Cloudflare Tunnel, Jellyfin never needs to accept inbound traffic at all, but it still gets HTTPS and a public address.
Prerequisites Before You Touch Any Config
Before installing a single container, I always run through this checklist. Skipping it is the number-one reason setups fail on day one. You need a domain name, a working DNS record, a static local IP for your Jellyfin host, and confirmation that you actually have a public IPv4 address (CGNAT will ruin your day otherwise).
Get a domain and point it at your home
Buy a cheap domain from Cloudflare, Namecheap, Porkbun, or any registrar you trust. Then create an A record for jellyfin.example.com pointing to your public IPv4. If your ISP gives you a dynamic IP, install a DDNS client (DuckDNS, ddclient, or the dynamic DNS built into your router) so the record stays current.
Most reverse proxies include dynamic DNS updaters as add-ons. SWAG has a built-in DuckDNS + Let’s Encrypt combo that is hard to beat for beginners, and Nginx Proxy Manager has a Cloudflare DNS challenge option that handles wildcard certificates cleanly. Pick whichever matches where you bought your domain.
Reserve a local IP for the Jellyfin host
Open your router’s DHCP settings and bind your Jellyfin server’s MAC address to a fixed address like 192.168.1.50. Without this, your Docker host may move around and your reverse proxy will point to nothing.
I prefer to use a clean subnet layout: 192.168.10.0/24 for servers, 192.168.20.0/24 for trusted devices, and 192.168.30.0/24 for IoT. This is overkill for a one-server household, but it makes firewall rules much easier to reason about as you add services. For a single Jellyfin box, a simple DHCP reservation works fine.
Check whether you are behind CGNAT
CGNAT (Carrier-Grade NAT) is when your ISP shares one public IPv4 between many customers. If you are behind CGNAT, traditional port forwarding will never work, and you must use Tailscale, Cloudflare Tunnel, or a VPS-bridge approach. Many people discover this only after hours of router configuration, so check first.
To detect CGNAT, compare the public IP reported by an external service to the WAN IP shown in your router’s status page. Visit whatismyip.com from your home network, then log into your router. If the two addresses do not match, you are behind CGNAT.
For a quick command-line check on Linux:
curl -4 https://ifconfig.io
ip -4 addr show ppp0 | grep inet # or your WAN interfaceIf those two addresses differ, your ISP is double-NATing you. Call and politely ask for a public IPv4 (many ISPs will switch you on request), or skip directly to the no-open-ports section below.
Make sure local Jellyfin access works first
Open a browser on a device on your LAN and verify http://192.168.1.50:8096 loads the Jellyfin dashboard. If this fails, no amount of reverse proxy magic will save you. Lock in LAN access before adding external reachability.
I also create my admin user, set a real password, and add at least one library locally so I know playback works end to end. Trying to debug Jellyfin and the reverse proxy at the same time is a recipe for frustration, so eliminate variables in order.
Decide on Docker vs bare metal
I run everything in Docker because config files become portable and rollback is one line. If you prefer bare metal, the Nginx and SWAG examples translate directly, just change the file paths and skip the Docker-only directives.
For Docker, you will want a clean docker-compose.yml with a dedicated proxy network shared by the reverse proxy and Jellyfin. Bridge networks work fine for almost everyone. Macvlan is only needed for niche cases where you need every container to appear as a first-class host on the LAN, and host networking is only worth it when you need top performance and do not care about isolation.
Configure Jellyfin Itself Before the Proxy
Most people forget these three settings and then chase phantom proxy bugs for hours. Open http://your-ip:8096/web/index.html#!/settings, sign in as admin, and confirm the following before you even install the proxy.
Enable remote connections
Go to Dashboard > Networking. Make sure Allow remote connections to this server is checked and that Enable automatic port mapping is unchecked. The latter is what tries to punch a hole through your router using UPnP, and we do not want that. Leave Known Proxies empty for now; we will set it after the proxy is up.
Set the LAN networks
Right below the remote connections toggle is LAN networks. Set this to your home subnet, for example 192.168.0.0/16 or a more specific range like 192.168.1.0/24. This tells Jellyfin “do not require a login from these private IPs,” which is how your TV in the living room will auto-resume playback without a password prompt.
If you forget this, your smart TV apps will demand a login on every launch and the experience is awful. Get this right before you ever expose Jellyfin externally.
Plan the Base URL (optional but recommended)
Under Dashboard > Networking > Base URL, you can leave this empty or set it to /jellyfin. If you host other services on the same domain, setting a Base URL prevents conflicts and lets you reverse-proxy multiple apps behind one host. Most people leave it empty for a clean root domain experience, and that works fine.
One catch: if you set a Base URL after the fact, every client app needs to be reconfigured to use the new path. I recommend deciding once, on day one, and never changing it.
Method 1: Jellyfin Reverse Proxy With HTTPS (the Main Method)
This is the configuration I recommend for 90 percent of home users. You pick a reverse proxy, install it in Docker, point your domain at it, and let it issue a free Let’s Encrypt certificate automatically. Below are the three I actually use, ordered from easiest to most flexible.
Option A: Nginx Proxy Manager (easiest, GUI-driven)
Nginx Proxy Manager (NPM) is a friendly web UI that wraps Nginx and Certbot. If you are new to reverse proxies, start here. The setup takes about 10 minutes and you can do almost everything from a browser.
Here is the docker-compose.yml I drop on the host:
version: "3.8"
services:
npm:
image: jc21/nginx-proxy-manager:latest
container_name: npm
ports:
- "80:80"
- "443:443"
- "81:81"
volumes:
- ./npm/data:/data
- ./npm/letsencrypt:/etc/letsencrypt
restart: unless-stopped
jellyfin:
image: jellyfin/jellyfin:latest
container_name: jellyfin
volumes:
- ./jellyfin/config:/config
- ./jellyfin/cache:/cache
- /path/to/media:/media
restart: unless-stopped
# No host ports! Only reachable via the proxy.After docker compose up -d, browse to http://your-server-ip:81. The default login is [email protected] / changeme. Immediately change the password and the email address. Then go to Hosts > Proxy Hosts > Add Proxy Host and fill in:
Domain:
jellyfin.example.comScheme: http
Forward Hostname/IP:
jellyfin(the Docker service name) or192.168.1.50Forward Port: 8096
Block Common Exploits: on
Websockets Support: on (critical for live sync and playback status)
Switch to the SSL tab and pick Request a new SSL Certificate from Let’s Encrypt. Tick Force SSL and HTTP/2 Support. NPM will validate your domain and hand back a valid certificate within a minute. Auto-renewal is built in, so you never have to think about certs again.
Option B: SWAG (LinuxServer’s hardened Nginx)
SWAG (Secure Web Application Gateway) is what I use on my own setup. It is Nginx preconfigured with sane defaults, fail2ban, CrowdSec, and a huge library of community proxy configs. SWAG is a LinuxServer.io image, so the configuration style is very consistent across containers.
The Docker Compose:
version: "3.8"
services:
swag:
image: lscr.io/linuxserver/swag:latest
container_name: swag
cap_add:
- NET_BIND_SERVICE
environment:
- PUID=1000
- PGID=1000
- TZ=America/New_York
- URL=jellyfin.example.com
- SUBDOMAINS= # leave blank for main domain
- VALIDATION=http # or dns-cloudflare for wildcard certs
- [email protected]
- ONLY_SUBDOMAINS=false
- DOCKER_MODS=linuxserver/mods:swag-auto-reload
volumes:
- ./swag/config:/config
ports:
- 443:443
- 80:80
restart: unless-stopped
jellyfin:
image: jellyfin/jellyfin:latest
container_name: jellyfin
volumes:
- ./jellyfin/config:/config
- ./jellyfin/cache:/cache
- /path/to/media:/media
restart: unless-stoppedSWAG ships with pre-written proxy configs in /config/nginx/proxy-confs/. The Jellyfin one is named jellyfin.subdomain.conf.sample. Rename it to jellyfin.subdomain.conf and SWAG will auto-reload. The default works out of the box, but I usually tweak the headers for better security and to satisfy some smart TV apps:
server {
listen 443 ssl;
server_name jellyfin.example.com;
include /config/nginx/ssl.conf;
client_max_body_size 0;
location / {
include /config/nginx/proxy.conf;
resolver 127.0.0.11 valid=30s;
set $upstream_app jellyfin;
proxy_pass http://$upstream_app:8096;
}
# Important for WebSocket support on /socket endpoint
location /socket {
include /config/nginx/proxy.conf;
resolver 127.0.0.11 valid=30s;
set $upstream_app jellyfin;
proxy_pass http://$upstream_app:8096;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}The proxy.conf includes the right X-Forwarded-For, X-Real-IP, and Host headers automatically. Without these, Jellyfin sees the proxy’s IP for every request, which breaks bandwidth limiting and rate limiting. This is one of the most common causes of “Jellyfin behind reverse proxy misbehaves” threads on Reddit.
SWAG also bundles fail2ban, so brute-force attacks on the Jellyfin login are auto-banned. CrowdSec is available as an optional mod that adds behavior-based intrusion detection, which I have started using in 2026 because it catches slow distributed attacks better than fail2ban alone.
Option C: Traefik (declarative, label-driven)
Traefik is what I run when I want zero manual config files and full Docker integration. You define routes via labels on each container, and Traefik handles certificate issuance, routing, and middleware entirely on its own. The downside is a steeper learning curve and slightly less community documentation for niche Jellyfin apps.
version: "3.8"
services:
traefik:
image: traefik:v3.1
container_name: traefik
command:
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
- "--entrypoints.web.http.redirections.entryPoint.to=websecure"
- "[email protected]"
- "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
- "--certificatesresolvers.letsencrypt.acme.tlschallenge=true"
ports:
- "443:443"
- "80:80"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./traefik/letsencrypt:/letsencrypt
restart: unless-stopped
jellyfin:
image: jellyfin/jellyfin:latest
container_name: jellyfin
volumes:
- ./jellyfin/config:/config
- ./jellyfin/cache:/cache
- /path/to/media:/media
labels:
- "traefik.enable=true"
- "traefik.http.routers.jellyfin.rule=Host(`jellyfin.example.com`)"
- "traefik.http.routers.jellyfin.entrypoints=websecure"
- "traefik.http.routers.jellyfin.tls=true"
- "traefik.http.routers.jellyfin.tls.certresolver=letsencrypt"
- "traefik.http.services.jellyfin.loadbalancer.server.port=8096"
restart: unless-stoppedTraefik picks up the labels automatically, requests a certificate, and routes traffic to Jellyfin. For WebSocket support you usually do not need any extra config because Traefik detects the upgrade headers by default.
Open ports 80 and 443 on your router
Whatever proxy you choose, you need to forward external ports 80 and 443 to the proxy host’s LAN IP. Modern browsers will silently fail HTTPS if they cannot reach the ACME challenge on port 80, so do not skip it. Inside your router, set up:
External 80 → Internal 80 (proxy host) — for Let’s Encrypt HTTP-01 challenges
External 443 → Internal 443 (proxy host) — for HTTPS traffic
Do not forward 8096. That is the whole point of this architecture.
If you cannot open any ports at all (CGNAT, apartment Wi-Fi, workplace restrictions), skip straight to Method 2 below.
Go back to Jellyfin and set Known Proxies
Now that traffic flows through the proxy, return to Dashboard > Networking and update Known Proxies with the Docker network subnet or the proxy container’s IP. For SWAG, Traefik, or NPM running on the same Docker host, 172.17.0.0/16 or your custom bridge subnet works.
This tells Jellyfin to trust X-Forwarded-For from these addresses, which fixes the “Jellyfin sees proxy IP instead of real client IP” symptom. Without it, bandwidth limiting per user becomes useless because every user appears to come from one IP.
Method 2: No-Open-Ports Alternatives (Tailscale, Cloudflare Tunnel, VPN)
Sometimes you cannot or should not expose anything to the internet. CGNAT, paranoid employers, paranoid ISPs, and shared housing all push you toward the no-open-ports approach. These methods work brilliantly for personal use and most family setups, with the caveat that smart TV support varies.
Tailscale: dead-simple mesh VPN
Tailscale installs in about a minute on every device and creates a WireGuard-backed mesh network. Jellyfin listens only on the Tailscale interface, your devices join the tailnet, and that is it. There is no port forwarding, no DDNS, no certificate management. It is the easiest Jellyfin remote access without port forwarding option for laptops and phones.
To use Tailscale with Jellyfin, install tailscale on the Jellyfin host, then bind Jellyfin to the Tailscale interface. In Docker Compose, that looks like:
services:
jellyfin:
image: jellyfin/jellyfin:latest
network_mode: host # so it can see the Tailscale interface
volumes:
- ./jellyfin/config:/config
- ./jellyfin/cache:/cache
- /path/to/media:/media
restart: unless-stoppedOn each client, install Tailscale, log in to the same account, then point the Jellyfin app at http://jellyfin-host:8096 using its Tailscale IP (100.x.x.x) or its MagicDNS name. Playback is full speed because traffic stays on the WireGuard tunnel.
The downside is that family members have to install Tailscale too, and smart TVs (especially WebOS and Tizen) often cannot run it. For those devices, fall back to the reverse proxy or expose only Jellyfin via the proxy while keeping the LAN media shares private.
One tip that has saved me a lot of explaining: enable MagicDNS in the Tailscale admin panel so devices resolve human-friendly names like jellyfin-host instead of having to remember a 100.x.x.x IP. Family members are far more willing to type a name than a string of numbers.
Cloudflare Tunnel: zero inbound ports, free HTTPS
Cloudflare Tunnel is the best fit when you want zero open ports and you are comfortable routing traffic through Cloudflare’s network. It works on devices that cannot run a VPN client (smart TVs, Apple TV, Fire TV), because the client devices only need to reach jellyfin.example.com on the public internet.
Setup is a three-step dance:
Add your domain to Cloudflare (free tier is fine).
Create a tunnel in the Cloudflare Zero Trust dashboard, copy the token.
Run
cloudflaredas a Docker container on your Jellyfin host.
The Docker Compose for cloudflared:
services:
cloudflared:
image: cloudflare/cloudflared:latest
container_name: cloudflared
command: tunnel --no-autoupdate run
environment:
- TUNNEL_TOKEN=eyJhIjoixxxxxxxxxxxx
restart: unless-stopped
network_mode: host # for best performanceIn the Cloudflare dashboard, configure a public hostname jellyfin.example.com pointing to http://jellyfin:8096. Cloudflare handles HTTPS automatically and never opens a port on your router. For an extra layer of polish, enable Cloudflare Access on the hostname to require email or one-time-passcode login before users hit Jellyfin’s own login page.
One caveat to know going in: Cloudflare’s free tier technically limits large streaming transfers, so if you are pushing 4K HDR with subtitles to multiple users simultaneously, expect occasional buffering. For 1080p home streaming to one or two devices, you will never notice.
WireGuard / OpenVPN: the classic VPN route
For tech-savvy users who want full control, a self-hosted WireGuard server (or OpenVPN) on the same host as Jellyfin gives you an encrypted tunnel and zero public exposure. WireGuard has the edge in performance and simplicity; OpenVPN is more universally compatible with old devices.
Install WireGuard on the Jellyfin host, generate keys, and forward the UDP port of your choice (51820 is conventional). On each client, import the config file and connect. Then use http://jellyfin-host:8096 over the WireGuard IP. It is more work than Tailscale, but it costs nothing, runs entirely on your hardware, and supports 4K streaming without third-party relays.
I prefer Pi-hole’s pivpn installer or wg-easy for the server side. Both have web UIs, both generate client QR codes for phones, and both integrate cleanly with Jellyfin.
Which no-open-ports method should you pick?
Here is how I decide for friends who ask. Tailscale for personal-only access across laptops and phones. Cloudflare Tunnel for households with smart TVs where you want zero open ports. Self-hosted WireGuard if you are comfortable with Linux, want full ownership of the path, and do not want any third party in the middle.
Advanced: Reverse proxy on a VPS with a tunnel back home
If you have CGNAT but still want the clean reverse-proxy experience with full streaming performance, rent a small VPS (DigitalOcean, Hetzner, OVH) for a few dollars a month and run the proxy there. A WireGuard tunnel connects the VPS to your Jellyfin host at home, and the VPS handles the public HTTPS termination. This is exactly the architecture I run, and it gives you a public IP, fast Let’s Encrypt issuance, and a firewall in front of everything.
The VPS-side docker-compose.yml looks just like the SWAG example above, but instead of pointing at a local Jellyfin container it points at the WireGuard tunnel IP (something like 10.50.0.2:8096). Home users get the same clean Jellyfin reverse proxy experience as if they had a public IP, without ever dealing with CGNAT.
Troubleshooting the Stuff That Breaks
Even with a clean setup, Jellyfin behind a reverse proxy throws curveballs. These are the fixes that resolve roughly 90 percent of the “it doesn’t work” posts I see on r/jellyfin.
The login loop (you keep getting bounced back to the login page)
This is almost always one of three things:
Missing or wrong X-Forwarded headers. Without
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;andproxy_set_header X-Forwarded-Proto $scheme;, Jellyfin thinks the request is HTTP and rejects the secure cookie.Known Proxies is empty or wrong. Jellyfin will not trust the proxy until you list its IP in the Known Proxies field under Networking.
Mixed content. If Jellyfin thinks the scheme is HTTPS but a subresource loads over HTTP, the browser blocks the cookie. Make sure
X-Forwarded-Proto httpsis set.
If all three are correct and the loop persists, clear cookies on the client and try an incognito window. Old cookies from a previous HTTP setup can confuse Jellyfin even after the proxy is correct.
WebUI works but the apps do not connect
This is the single most common Reddit complaint I see. The web UI is forgiving because the browser handles WebSockets, cookie domain quirks, and CORS gracefully. Native apps (especially the Android TV and iOS clients) are stricter. Here is what to check:
Make sure WebSockets are passed through. Both Nginx Proxy Manager and SWAG need explicit WebSocket support, which is usually a single checkbox or a
proxy_set_header Upgradepair.Use the domain, not the IP. Apps sometimes refuse to negotiate TLS when the cert is for a hostname but the user enters an IP.
Do not put Jellyfin behind a path prefix unless the app supports it. Most apps assume
https://jellyfin.example.com, nothttps://example.com/jellyfin. If you need the prefix, configure the Base URL in Jellyfin to match and test the desktop client first.Check the LAN networks setting. If your phone is on LTE and your Jellyfin LAN setting is too narrow, the app may refuse the connection.
For Android TV and Fire TV specifically, I have found that toggling the in-app “Use HTTPS” option off and on after connecting once fixes the first-launch handshake issue. WebOS TVs need CORS headers in the proxy config that some default setups miss.
CGNAT and double NAT
Both make traditional port forwarding impossible. The fix is to skip inbound forwarding entirely and use one of the no-open-ports methods above. CGNAT specifically: call your ISP, ask for a public IPv4 (often free), or move to Tailscale, Cloudflare Tunnel, or the VPS-bridge approach.
Double NAT is a related issue. If you have your ISP modem in router mode and your own router behind it, you are double-NATed even with a public IP. Fix it by putting the ISP modem into bridge mode and letting your own router handle everything.
Hairpin NAT (works on LTE but not home Wi-Fi)
This is the classic symptom: you connect on cellular data and Jellyfin loads fine, but from your own Wi-Fi, jellyfin.example.com times out. Your router does not support hairpin NAT, meaning it cannot route a request from inside the LAN back out to its own public IP.
The fix is to either enable hairpin NAT in the router (most pfSense, OPNsense, and Asus routers do this), or use DNS split-horizon: serve 192.168.1.50 to LAN clients and your public IP to the rest of the world. Most routers support this under “DNS Rebinding” or “NAT Loopback.” If yours does not, run Pi-hole locally and override the A record for jellyfin.example.com to your LAN IP.
Mixed content, subtitles, casting
If subtitles, casting, or thumbnails break while everything else works, you have mixed content. The fix is to make sure Jellyfin is being served over HTTPS end to end and that the proxy sets X-Forwarded-Proto https. Browsers silently drop subresources loaded over plain HTTP from a secure page, which is exactly the symptom you are seeing.
Also avoid putting Jellyfin on a non-standard HTTPS port behind the proxy unless every client app supports that port. Stick to 443 and save yourself hours of frustration.
WebSocket drops during playback
If Jellyfin’s live sync or playback status keeps disconnecting, your proxy is killing idle WebSocket connections. The fix is to bump the proxy timeouts: in Nginx add proxy_read_timeout 600s; and proxy_send_timeout 600s;, and in SWAG the default config already does this but verify by checking proxy.conf.
In Traefik the equivalent is to add a forwarding timeout in the entrypoint config. NPM has a default that is usually fine but worth checking if you upgrade.
SSL certificate errors after proxy setup
If you see a cert warning in your browser, the proxy either failed to issue the certificate or is serving a stale one. Check the proxy logs for the ACME challenge failure reason. Common causes: port 80 is not forwarded, your domain is not yet propagated, or Cloudflare proxying is enabled on a record that needs to be DNS-only during the HTTP challenge. Use the DNS-01 challenge with Cloudflare API credentials if HTTP-01 keeps failing.
Performance Tips for Remote Streaming
Once Jellyfin remote access works, the next bottleneck is usually your upload bandwidth and transcoding performance. These are the dials I tune to get smooth 1080p and 4K streaming over the internet.
Set a remote bitrate limit. In Dashboard > Playback > Streaming, set the remote bitrate limit to about 70 percent of your home upload speed. If you have 50 Mbps upload, cap remote streams at 35-40 Mbps to leave headroom for other devices. Jellyfin will transcode above that limit, which prevents buffering but increases CPU load.
Prefer direct play when possible. Direct play sends the file as-is with no transcoding, which is far lighter on your server and gives perfect quality. Match your media codecs to what your clients support: H.264 + AAC works on almost everything, while HEVC and AV1 only direct play on newer devices. Tools like Sonarr and Radarr can help you pick compatible release formats if you care about maximum compatibility.
Watch your subtitle format. PGS and ASS subtitles require transcoding on most clients. SRT subtitles direct play everywhere and add basically zero overhead. If remote performance is bad and you cannot figure out why, switch your default subtitle format to SRT and watch the difference.
Enable hardware transcoding if your CPU supports it. Intel QuickSync (iGPU), NVIDIA NVENC, and AMD AMF all offload transcoding to dedicated silicon, turning a struggling CPU into a smooth streaming box. In Jellyfin, enable the relevant option under Dashboard > Playback > Transcoding and pick the right device (e.g., /dev/dri/renderD128 for Intel QuickSync in Docker).
Security Hardening Checklist Beyond HTTPS
HTTPS is the floor, not the ceiling. These are the extra layers I always add.
fail2ban or CrowdSec. SWAG ships with fail2ban preconfigured. NPM does not; install CrowdSec via a sidecar container. Both ban IPs that fail logins repeatedly, which removes 95 percent of automated attacks.
Restrict Jellyfin’s listen address. If you only stream from your reverse proxy, restrict Jellyfin to listen on the Docker bridge network, not 0.0.0.0. Less surface area means fewer attack vectors.
Use a real admin password and enable 2FA. Jellyfin does not have built-in 2FA for end users, so put Authelia or Cloudflare Access in front of the login page. Authelia runs as a Docker container and slots neatly between your proxy and Jellyfin.
HSTS preload. Add the Strict-Transport-Security header with
max-age=63072000; includeSubDomains; preloadso browsers refuse to ever downgrade to HTTP.Block direct IP access. In Nginx, return 444 for any request whose Host header is not your domain. This stops scanners that probe your IP directly without knowing the hostname.
These steps together turn Jellyfin from “convenient self-hosted media server” into something reasonably close to commercial-grade streaming security.
Testing Checklist: How to Know It Actually Works
Once everything is configured, run through this short list before declaring victory. Each test catches a different failure mode that is otherwise invisible.
Local test. From a device on your LAN, open
https://jellyfin.example.com. If this works, your proxy and Jellyfin are talking. If not, check Known Proxies and forwarded headers.External LTE test. Disconnect from Wi-Fi on your phone and load the same URL. If this works, your port forwarding and DNS are correct. If not, double-check ports 80 and 443 on your router.
Hairpin test. Back on your home Wi-Fi, load the URL again. If LTE works but home Wi-Fi fails, you have a hairpin NAT issue (see troubleshooting).
Certificate test. Click the padlock icon in your browser and confirm the cert is issued by Let’s Encrypt with a valid date range. If you see a warning, your ACME challenge failed.
App test. Install the Jellyfin app on a phone or TV, add the server using
https://jellyfin.example.com, and try playing a file. If the web UI works but the app does not, revisit the WebSocket and Base URL sections above.Subtitle and casting test. Try turning on subtitles and casting to a TV. If either breaks, you have a mixed-content issue (HTTPS not enforced end to end).
If all six pass, your setup is solid. Add a Uptime Kuma or Healthchecks.io monitor on https://jellyfin.example.com/web/touch.html so you get a notification the moment it breaks.
Frequently Asked Questions
How do I access Jellyfin remotely without port forwarding?
Use Tailscale, Cloudflare Tunnel, or a self-hosted WireGuard server. All three keep every inbound port on your router closed. Tailscale is the easiest for personal devices, Cloudflare Tunnel works for smart TVs too, and WireGuard gives you full control.
Which reverse proxy is best for Jellyfin: Nginx, SWAG, or Traefik?
Nginx Proxy Manager is the easiest for beginners thanks to its GUI. SWAG is my pick for power users who want pre-tuned Nginx plus fail2ban. Traefik is the best fit if you run many Docker services and want zero config files. All three work fine for Jellyfin remote access.
Do I need to open port 8096 for Jellyfin remote access?
No. Open only ports 80 and 443 on your router and point them at your reverse proxy. Jellyfin keeps port 8096 bound to your LAN or Docker network only. This is the whole point of putting Jellyfin behind a reverse proxy.
How do I fix the Jellyfin login loop when using a reverse proxy?
Set X-Forwarded-For and X-Forwarded-Proto headers in your proxy, then add the proxy IP to Jellyfin’s Known Proxies under Dashboard u0026gt; Networking. Make sure Force SSL is enabled in the proxy. These three changes fix nearly every login loop case.
Can I use Cloudflare Tunnel or Tailscale with Jellyfin?
Yes to both. Cloudflare Tunnel needs your domain on Cloudflare plus a small cloudflared container. Tailscale works on any device that can install the client and is the fastest way to set up Jellyfin remote access without port forwarding.
What are the CGNAT workarounds for Jellyfin remote access?
Three options: call your ISP and request a public IPv4, switch to Tailscale or Cloudflare Tunnel to bypass port forwarding entirely, or rent a cheap VPS and use it as a WireGuard bridge back to your home network. Most home users go with Tailscale.
Is a reverse proxy or VPN better for Jellyfin remote access?
A reverse proxy works on every device including smart TVs and is best for families. A VPN is simpler for personal use across laptops and phones, gives you zero open ports, and avoids certificate management. Pick reverse proxy for shared setups, VPN for personal ones.
How do I configure WebSockets for Jellyfin behind a reverse proxy?
In Nginx add proxy_set_header Upgrade $http_upgrade and proxy_set_header Connection u0022upgradeu0022 inside a location block for /socket, then turn on Websockets Support in Nginx Proxy Manager. Without these, live sync and playback status break.
Wrapping Up
Putting Jellyfin behind a reverse proxy is the single biggest security upgrade you can make to a home media server, and it also unlocks a clean jellyfin.example.com address on every device you own. For most readers, SWAG or Nginx Proxy Manager plus Let’s Encrypt is the right starting point, with Tailscale as a personal backup and Cloudflare Tunnel as the no-open-ports escape hatch when CGNAT or strict networks get in the way.
From here, set up automated library scans, configure hardware transcoding if your CPU supports it (Intel QuickSync or NVIDIA NVENC), and consider a small monitoring stack like Uptime Kuma so you get notified the moment your remote access breaks. That is everything you need for safe, fast Jellyfin reverse proxy remote access in 2026 and beyond.