What's actually happening
Your home router gives every device on the LAN a private IP — 192.168.1.42, 10.0.0.5, things in those reserved blocks. None of those addresses are reachable from the public internet; the entire LAN sits behind one shared public IP that the router holds.
When your laptop opens a connection out (to lrok.io, to GitHub), the router NATs the connection — it remembers "laptop:54231 → lrok.io:443" and rewrites the source IP/port so the response comes back to the right device.
NAT works outbound. It does not work inbound. If a random server on the internet wants to send a packet to your laptop, the router has no entry in its NAT table for that connection and drops it.
Port forwarding is the manual override: "for incoming TCP on port 25565, ignore NAT, send it to 192.168.1.42:25565." Now Minecraft players can connect.
Setting it up
Every router has a "Port Forwarding" or "Virtual Server" page. You specify:
- The external port (what the public IP receives on, e.g. 25565)
- The internal IP (which LAN device, e.g. 192.168.1.42)
- The internal port (what port on that device, often the same)
- The protocol (TCP, UDP, or both)
Once you save, traffic to your-public-ip:25565 lands on your laptop's port 25565.
Why this stopped working
Two reasons:
Carrier-grade NAT (CGNAT). Most residential ISPs in 2026 — and almost every mobile carrier — put hundreds or thousands of customers behind one shared public IPv4. The "public" IP your modem reports is itself a private IP; there's another layer of NAT at the ISP. Port forwarding on your router doesn't help because your router's WAN side is also private. You can't punch a hole through a NAT you don't control.
T-Mobile home internet, Verizon 5G home, Starlink, Comcast in some markets — all CGNAT by default. The fix is "static IP" service (often $10-20/mo extra) which puts you back on a real public IPv4.
IPv4 exhaustion. ISPs are out of v4 addresses; they don't hand out more unless you pay. IPv6 works (no NAT, every device gets a real address) but only when both endpoints support it — which most public services in 2026 still do, but inconsistently.
Why a reverse tunnel solves the same problem differently
A reverse tunnel inverts the direction. Instead of accepting incoming connections (which you can't if you're behind CGNAT), your machine opens an outbound connection to a relay, and the relay forwards traffic back through that pipe.
[player on internet] -> [tunnel relay] -> [open conn] -> [your Minecraft server]
The relay has a real public IP. It's the relay's port forwarding, not yours. You don't need router access, you don't need a static IP, you don't need to fight CGNAT. The cost is one outbound connection from your machine to the relay (which CGNAT lets through happily — that's what NAT is built for).
This is what services like lrok do. lrok tcp 25565 opens an outbound multiplexed connection from your laptop, registers a public TCP endpoint, and forwards traffic in. Players connect to tcp.lrok.io:31420, the bytes traverse the relay, your Minecraft server sees a normal localhost connection.
When port forwarding still wins
If you control the router, your ISP gives you a real IPv4, and you don't mind exposing your home IP to the world — port forwarding is free, has no extra latency, and doesn't depend on a third party. Self-hosted home servers behind business-class internet (or a fiber connection that includes a static IP) work fine.
If any of those conditions fail, a tunnel is the simpler answer.
Local concerns either way
Whatever method you use, the moment your service is on the public internet, scanners will hit it within hours. A 30-day-old IPv4 with no firewall sees 100k probes per day on common ports (22, 80, 3389, 3306). Treat any tunnel-exposed or port-forwarded service as if it were on the open internet — because it is.
For dev work, prefer named subdomains with reasonable security (basic-auth on lrok, OAuth proxies, signed-URL expiry) over public ports for everything. The Discord-bot guide and the stripe-webhook guide both walk through that pattern.