The scenario is painfully familiar. You set up Jellyfin or Immich at home, port-forward on the router, enter your address on a mobile data connection — and silence. The port forward was triple-checked, the firewall is off, the server inside the network responds perfectly. But from outside — nothing.
Almost always it’s CGNAT. Your provider didn’t give you a public address, but placed you behind a shared translation node with hundreds of neighbors. The good news: you can bypass this in five different ways, and three of them are free. The bad news: each has its own price, and you must choose wisely, because redoing it later hurts.
Let’s break it down step by step: first diagnosis, then all five routes with honest limitations, and finally a decision matrix and pitfalls.
Part 1. Five-minute diagnosis
Test one: what the router sees
Open the router’s web interface and look at the WAN address. Then open on any device on the same network ifconfig.me or similar and see the address the internet sees you as.
- Addresses match → you have a white IP. The problem is not CGNAT; look for a mistake in port forwarding or in the firewall on the server itself.
- Router address starts with
100.64–100.127→ this is Shared Address Space. Very likely CGNAT. - Router address in
10.x.x.x,172.16–31.x.x,192.168.x.x→ also gray: the provider NATs on private addresses.
Range 100.64.0.0/10 is allocated by RFC 6598 precisely for Carrier-Grade NAT — for numbering points of attachment between the CGN node of the provider and the subscriber’s equipment. It explicitly states: packets with such addresses should not cross operator boundaries, and such addresses should not reach external DNS zones. In other words, by definition this is not something you can reach from outside.
Test two: traceroute
From your home machine run traceroute 8.8.8.8 (or tracert in Windows). If the first hop is your router, the second is an address from 100.64/10 or private, and only the third shows something public, that confirms it: there’s another NAT between you and the internet. Classic NAT444.
Test three: ask the provider
The most underrated step. Script for support: “I need a dedicated public IPv4 address, white IP service. Also confirm whether IPv6 is available on my plan.” The second question is more important than the first — we’ll come back to it.
Also check IPv6 right now: open test-ipv6.com or run curl -6 https://ifconfig.co. If you get a response, you might not have a problem at all — you just solved it in the wrong protocol.
Part 2. What exactly breaks
It’s useful to understand the scale, because CGNAT breaks not only port forwarding. RFC 6598 honestly lists NAT444 drawbacks: console games don’t work, video calls degrade, p2p breaks, geo-location lies. Add to this:
- Port forwarding is physically impossible. It may be configured on your router and even show “active,” but the incoming packet will never reach the router.
- DDNS is pointless. It will update the A record to an address shared by hundreds of subscribers.
- UPnP and NAT-PMP don’t help. They negotiate with your router, not with the provider’s equipment.
- External captchas and bans hit you because of neighbors. One noisy neighbor on the shared link, and you’ll face Cloudflare checks for no reason.
All five routes below rely on the same principle: since the inbound connection doesn’t reach you, the host must initiate the connection from the inside. Outward CGNAT allows everything through. The routes differ only in who acts as the meeting point and what it costs you.
Part 3. Route 1 — ask for a white IP
The dullest solution and the most underrated.
The “dedicated/static IP” service exists with almost all wired providers. In the Russian market this is usually around 100–200 ₽/month, but check with your provider — rates change, and I’m not stating them as facts.
When to take it: if you have a decent home uplink, you want Jellyfin with direct 4K sharing, you play on the network, or you host something where latency and bandwidth matter. No tunnel will beat a direct connection.
When not to take it: mobile internet as the primary channel (white IP is usually not sold there or is expensive), or you’re not comfortable exposing your home address publicly.
A white IP means your home server is now directly accessible to the entire internet, including scanners. About an hour after activation you’ll see SSH probe attempts in the logs. Do not open port 22 externally; use key-based authentication, and keep everything else behind a reverse proxy with TLS.
Part 4. Route 2 — IPv6
If the provider gives IPv6, you don’t have CGNAT problems: there are enough addresses for everyone, and each device in the home gets a globally routable address. NAT in IPv6 is unnecessary.
What to do:
- Make sure the router gets a prefix (usually /64 or /56) and distributes it on the LAN.
- Allow inbound on the needed port in the router’s firewall. This isn’t port forwarding — there’s no translation, the server address is already global. But a stateful firewall by default blocks inbound, and that’s correct.
- Set up DDNS with an AAAA record: the prefix for residential subscribers is often dynamic.
The main limitation of IPv6 is not on your side, but on the client side. If you’re in a cafe or roaming on a network that has no IPv6, you won’t reach your IPv6 address. In practice IPv6 works as a “primary path plus a fallback” rather than the sole solution. Keep a second route from this list in your pocket.
Part 5. Route 3 — overlay network
Tailscale, NetBird, ZeroTier, self-hosted Headscale. Idea: all your devices establish outgoing connections to a coordinating server, which helps them find each other and, if possible, establish a direct p2p channel (NAT traversal); if that fails, traffic goes through a relay.
This is the fastest way to get a working result: setup on both sides takes about five minutes, you don’t need to touch the router at all.
Tailscale pricing at the time of writing: Personal plan — free, up to 6 users, unlimited user devices, 50 tagged resources and up to 3 ACL groups. Paid plans — Standard $8 and Premium $18 per user per month. Numbers from the pricing page and, of course, may change.
When overlay is the right choice: access is needed for you and your people, not the public. Home Assistant from mobile, SSH to the server, Immich for family, NAS access from vacation. Classic tasks where a public address is not required at all.
The downside: the coordinating server knows your network topology and participates in key exchange. Tailscale traffic is end-to-end encrypted via WireGuard and does not travel in the clear through their servers, but metadata — which devices, when online, who talks to whom — passes through the company’s infrastructure. If this is unacceptable, there is Headscale — an open-source coordinating server implementation that you run yourself. Clients remain official.
When overlay is not suitable: if you specifically need public access. You cannot share a link to your blog or page via Tailscale — the other side needs a client and authentication.
Part 6. Route 4 — Cloudflare Tunnel and its ceiling
cloudflared creates an outbound connection from your network to Cloudflare, after which the domain served by Cloudflare starts serving your local service. A public address is not required, ports don’t need to be opened, TLS certificate is obtained automatically. Supports HTTP, SSH, RDP.
It sounds perfect, and for sites, dashboards, and APIs it really works very well. But the solution has two hard ceilings, which people usually discover after moving in.
Ceiling one: video is prohibited by rules. Cloudflare states this unequivocally: «From the very beginning we prohibited streaming video using our bandwidth». The restriction applies to Free, Pro and Business plans; the legal way to distribute video is Cloudflare Stream. And there is a warning that if violated, content may be redirected or “taken other actions to protect service quality”.
Jellyfin, Plex, Emby, or Immich with video libraries via Cloudflare Tunnel on the free plan — this is a direct violation of Cloudflare’s rules, not a “gray area.” It will work only until someone notices. For a media server, use any other method described in this article.
Ceiling two: maximum upload file size. The maximum request body size through Cloudflare proxying is 100 MB on Free and Pro plans, 200 MB on Business, 500 MB by default on Enterprise. Exceeding it returns 413 Request entity too large. For Nextcloud, Immich and any service where you upload large files, this is a death sentence — photos from a phone will go through, videos from a drone won’t.
What’s still a plus: your home address is fully hidden, DDoS protection is included, and there is Cloudflare Access for authentication before the app. There are practical analyses on the forum already — setting up Cloudflare Tunnel and integration with Remnawave via Docker Compose.
Part 7. Route 5 — your VPS relay
The most flexible option: rent the cheapest VPS for its white IP, establish WireGuard between the VPS and your home server, and wrap the necessary ports in a tunnel on the VPS.
Step 1. Keys
On both sides:
wg genkey | tee privatekey | wg pubkey > publickey
Step 2. Config on the VPS
/etc/wireguard/wg0.conf:
[Interface]
Address = 10.8.0.1/24
ListenPort = 51820
PrivateKey = <private_key_VPS>
[Peer]
# home server
PublicKey = <public_key_home>
AllowedIPs = 10.8.0.2/32
Endpoint on the peer intentionally not specified: home address is unknown and changes, the connection is always initiated by home.
Step 3. Config at home
[Interface]
Address = 10.8.0.2/24
PrivateKey = <private_key_home>
[Peer]
PublicKey = <public_key_VPS>
Endpoint = 203.0.113.10:51820
AllowedIPs = 10.8.0.0/24
PersistentKeepalive = 25
PersistentKeepalive = 25 on the home side is not optional, but mandatory. Without it, CNAT translation entry in the provider’s CGNAT expires after a minute or two of downtime, and the tunnel drops before the next outgoing packet. Symptom: “it works while I’m actively using it, but in the morning it’s unavailable.”
Set up on both sides: wg-quick up wg0, enable on boot — systemctl enable wg-quick@wg0. Check with wg show and ping 10.8.0.2 from the VPS.
Step 4. Forward a port on the VPS
Enable forwarding:
echo 'net.ipv4.ip_forward = 1' > /etc/sysctl.d/99-forward.conf
sysctl --system
nftables rules:
table ip nat {
chain prerouting {
type nat hook prerouting priority dstnat;
iifname "eth0" tcp dport 443 dnat to 10.8.0.2:443
}
chain postrouting {
type nat hook postrouting priority srcnat;
oifname "wg0" masquerade
}
}
It works — but with an unpleasant side effect: because of masquerade, the home server appears as the source 10.8.0.1, and the real client IP is lost. All IP restrictions, geo rules, and fail2ban on the home side become useless.
Step 5 (better): nginx stream instead of DNAT
If the service uses HTTPS, instead of pure DNAT use nginx on the VPS in stream mode with PROXY protocol:
stream {
server {
listen 443;
proxy_pass 10.8.0.2:8443;
proxy_protocol on;
}
}
And on the home nginx:
server {
listen 8443 ssl proxy_protocol;
set_real_ip_from 10.8.0.1;
real_ip_header proxy_protocol;
# ...
}
This ensures the real client address reaches the application, while TLS still terminates at home — the VPS only sees the encrypted stream. Important: if TLS terminates on the VPS, all traffic passes through an rented machine in clear text.
Ready-made wrappers for this same scheme exist if you don’t want to build it manually: Pangolin (discussion on the forum), rathole, frp. The idea remains the same — home agent, relay on a white IP, and differences lie in convenience and the availability of a web panel.
Part 8. Decision matrix
| Public access | Video/large files | Hides home IP | Latency | Price | |
|---|---|---|---|---|---|
| White IP | yes | unrestricted | no | minimal | ~100–200 ₽/mo |
| IPv6 | yes, but only for IPv6 clients | unrestricted | no | minimal | 0 |
| Overlay (Tailscale) | no, only for your own | relay-limited | yes | medium | 0 to 6 users |
| Cloudflare Tunnel | yes | video prohibited, upload ≤100 MB | yes | medium | 0 |
| VPS relay | yes | hits VPS limits | yes | +1 hop | from ~200 ₽/mo |
Short recommendations for typical tasks:
- Media server for yourself and family → white IP if available; otherwise VPS relay. Cloudflare Tunnel is out per the rules.
- Home Assistant from a phone → overlay. The simplest and safest option; you don’t need a public address here.
- Website, blog, control panel, API → Cloudflare Tunnel. Fast, free, with built-in protection.
- Immich or Nextcloud with video uploads → VPS relay or white IP: the 100 MB limit kills Cloudflare.
- Maximum privacy → Headscale on your own VPS: both coordinator and relay are yours.
Part 9. Seven pitfalls
1. Internal testing. Opened https://my-address from home network, it worked — this is hairpin NAT on the router, not external access. Only test with mobile data when Wi‑Fi is off.
2. Forgotten keepalive. See above. The most common cause of “it worked yesterday.”
3. MTU. WireGuard defaults to 1420. With double encapsulation (e.g., tunnel over PPPoE) this is not enough. Symptom: SSH and ping work, but HTTPS pages stall mid-load. Fix: MTU = 1380 in the [Interface] section, then tune.
4. Lost real client IP. After DNAT with masquerade fail2ban starts banning 10.8.0.1, i.e., the tunnel itself. Solution — PROXY protocol, as above.
5. Exposed SSH on the relay. The VPS with a white IP is scanned around the clock. Keys, PermitRootLogin no, non-standard port as hygiene, fail2ban.
6. Everything in one place. Tempting to also host a proxy panel on the same VPS, but the relay is a machine whose address you expose outward. Separate roles.
7. Forgetting outbound VPS traffic. Cheap plans often have a 1–2 TB/mo limit. One evening of 4K streaming through the relay can push you over. Plan ahead.
How did you solve it — pay for a white IP, stick with overlay, or build a relay? And separately: who has a provider that actually gives a working IPv6, and did you manage to live with it without a second route?
Sources
- RFC 6598 — IANA-Reserved IPv4 Prefix for Shared Address Space: range
100.64.0.0/10, constraints and NAT444 costs - Cloudflare Tunnel — documentation
- Cloudflare: Delivering videos with Cloudflare — ban on video streaming on Free/Pro/Business
- Cloudflare: limits on request body size — 100 / 200 / 500 MB by plan
- Tailscale Pricing — free plan details
- WireGuard — Quick Start
Related forum materials: home server roadmap, server from an old smartphone, Docker-ized networks for self-hosted.


