You bought a domain, pointed it at your home server, opened the port — and nothing. Welcome to CGNAT. Your ISP has put you behind a shared, carrier-grade NAT, so there is no public inbound port to forward. Some ISPs will open a single random port on request, which is how people end up sharing https://example.com:43117/ and hating it.
The good news: you can get clean, secure access to Jellyfin, Immich, and the rest without a public IP and without exposing port 443 on your home connection. The trick is to stop trying to accept inbound connections at home, and instead reach out to something that can accept them.
First, confirm you're actually behind CGNAT
Check the WAN IP your router shows, then compare it to your public IP (search "what is my IP"). If they differ — and especially if your router's WAN address is in 100.64.0.0/10 — you're behind CGNAT. Port forwarding will never work from the outside, no matter how the router is configured. That single check saves hours of debugging firewall rules that were never the problem.
The core idea: relay, don't expose
Every workable option follows the same shape: a machine with a real public IP accepts connections, and your home server maintains an outbound tunnel to it. Inbound at home stays fully closed. The options differ in who runs that public machine and how private the traffic stays.
Option 1 — Your own VPS as a reverse-proxy relay (most control)
Rent a small VPS, connect it to your home server over WireGuard, and run a reverse proxy (Caddy, Traefik, or nginx) on the VPS. The proxy terminates TLS for media.example.com and forwards over the WireGuard tunnel to Jellyfin/Immich at home.
- Clean domains, real Let's Encrypt certs, no ugly ports.
- Nothing inbound is open at home; the VPS only talks to your origin through the encrypted tunnel.
- You choose the VPS region (useful for latency and for keeping data in a jurisdiction you trust).
- Trade-off: you operate the VPS and pay for its bandwidth — relevant for high-bitrate Jellyfin streaming.
Option 2 — Cloudflare Tunnel (fastest to set up, with a caveat)
cloudflared makes an outbound tunnel to Cloudflare's edge; you get a clean domain and TLS with almost no config and no VPS to manage.
The caveat that keeps coming up in self-host threads: Cloudflare terminates TLS at their edge, so your traffic is decrypted there, and Cloudflare's terms restrict heavy media streaming through the free proxy. For dashboards, photos, and light use it's excellent. For a privacy-sensitive Immich library or heavy Jellyfin streaming, many people are uncomfortable routing plaintext media through a third party — which points back to Option 1 or 3.
Option 3 — Tailscale (and Serve/Funnel) for private and family access
If access is mostly for you and a few people, a WireGuard-based mesh like Tailscale is often the cleanest answer: install it on the server and on each client, and the apps are reachable over the private tailnet with no public exposure at all.
- Tailscale Serve gives those tailnet clients HTTPS with a real cert on a
*.ts.netname — no public ingress. - Tailscale Funnel can expose a service publicly through Tailscale's edge when you genuinely need a non-tailnet visitor to reach it.
- For a fully self-hosted control plane, Headscale replaces Tailscale's coordination server while keeping the same clients — the move several Synology users make to get off the hosted control plane without exposing the NAS.
A recurring gotcha: a Jellyfin/Docker setup works on one tailnet but breaks when shared across another tailnet. Cross-tailnet sharing has its own ACL and DNS rules — if a shared user can't connect, check the share's ACLs and that MagicDNS resolves the target on their side before assuming the container is misconfigured.
Option 4 — Keep it VPN-only (most private)
If nobody outside your household needs access, the simplest secure answer is to expose nothing. Run WireGuard (or Tailscale) and require clients to be on the VPN to reach any service. No public domain, no edge, no third party in the path. The cost is that every device must run a VPN client — fine for you, sometimes a hard sell for non-technical family members.
A decision tree
- Want a clean public domain and full control over the path? VPS relay + WireGuard + reverse proxy (Option 1).
- Want it working in 15 minutes for dashboards/light use? Cloudflare Tunnel (Option 2) — accept edge TLS termination.
- Access is just you and a few trusted people? Tailscale/Headscale (Option 3); use Serve for HTTPS, Funnel only when you must go public.
- Maximum privacy, no public surface at all? VPN-only (Option 4).
Protect the private media traffic
The whole reason CGNAT workarounds get debated so heavily is that media is sensitive. Two rules keep it clean:
- Don't let an unencrypted hop touch your library. If you use an external edge that terminates TLS, assume that operator can see the content. For private photos and home video, prefer a path where TLS terminates on hardware you control (your VPS or your own device) — Options 1, 3, and 4 all qualify.
- Separate "clean URL" from "who can reach it." A nice domain is about ergonomics; access control is a separate decision. Even with a public domain, gate sensitive apps behind auth (an auth proxy, or the app's own SSO) rather than relying on obscurity.
Recommended setups for common cases
- Immich for the family, privacy-first: VPS relay + WireGuard + Caddy, or Tailscale with Serve. Avoid plaintext third-party edges.
- Jellyfin streaming to a few people: VPS relay (watch bandwidth/region) or Tailscale; skip Cloudflare's proxy for heavy streams.
- A dashboard or two, low stakes: Cloudflare Tunnel is the least-effort clean-domain option.
- Just you, all devices you control: VPN-only is the smallest attack surface that exists.
The takeaway
CGNAT removes inbound ports, not your options. Stop trying to accept connections at home and instead maintain an outbound tunnel to something that can — a VPS you control, a tunnel provider, or a WireGuard mesh. Pick based on who needs access and how private the traffic must be, terminate TLS somewhere you trust, and you'll get clean https://media.example.com URLs with port 443 at home staying firmly shut.
From across the StoicSoft network
Hand-curated reads on the same topic from sister sites in the StoicSoft family.