DEV Community

Saptarshi Paul
Saptarshi Paul

Posted on

Bypass CGNAT: Reach Your Home Server or Raspberry Pi from Anywhere

You configured a home server or Raspberry Pi, forwarded a port on your router, and tested it from your phone's mobile data. Nothing loads. The router rule, firewall, and service all check out. The problem might not be on your side.

If your ISP uses Carrier-Grade NAT (CGNAT), your router never gets a publicly reachable IP address. No router setting fixes that. This guide explains how to confirm CGNAT, why port forwarding fails, and how to reach your server anyway.

Summary

If your router's WAN IP does not match the public IP from a "what is my IP" search, or it sits in 100.64.0.0/10, your ISP uses CGNAT and no port forward will ever receive traffic. The workaround is an outbound tunnel. Run ssh -p 443 -R0:localhost:8080 free.pinggy.io on the Pi to get a public HTTPS URL, or ssh -p 443 -R0:localhost:22 tcp@free.pinggy.io to expose its SSH port. Run that as a systemd service so it survives reboots, and lock down SSH before exposing anything.

What is CGNAT and why does it break home hosting?

Your home router uses NAT to let all devices share one public IPv4 address. IPv4 space is scarce, so many ISPs, especially mobile, satellite, and fixed-wireless providers, add a second NAT layer inside their own network. That is CGNAT.

With CGNAT, hundreds of customers share a single public IP. Your router gets a private address from the ISP, and the ISP translates it outbound. That works for browsing and streaming because your devices initiate connections. It fails for hosting because inbound connections cannot reach your router. The public IP belongs to the ISP, and there is no rule forwarding traffic to you.

In practice, CGNAT causes:

  • Self-hosted websites, dashboards, and home automation panels unreachable from outside
  • SSH to a Raspberry Pi only working on your local network
  • Game servers, media servers, and cameras inaccessible remotely
  • Webhooks failing to reach a home service

How to check whether you are behind CGNAT

Confirm it in about two minutes.

  1. Find your router's WAN IP. Log in to your router admin page (often 192.168.1.1 or 192.168.0.1) and look for the WAN or Internet IP on the status page.
  2. Find your public IP. Search "what is my IP" from any device on your network.
  3. Compare. If they match, you have a public IP and ordinary port forwarding should work. If they differ, you are almost certainly behind CGNAT.

A WAN address in 100.64.0.0 to 100.127.255.255 (100.64.0.0/10, the shared address space for carrier NAT) is a strong indicator. WAN addresses starting with 10., 172.16. through 172.31., or 192.168. also point to an extra NAT layer.

Why traditional port forwarding fails under CGNAT

The usual approach is to open a port on your router and point it at your server. A common variant is SSH port forwarding, where you route traffic through an SSH connection to reach a service on another machine. Both require a publicly reachable address somewhere in the path that can accept inbound connections.

Under CGNAT, you do not control that address. Your router cannot open a port on an IP it does not own, so the forwarding rule never sees traffic.

Your options for reaching a server behind CGNAT

  • Ask your ISP for a public IP. Some assign one, often for a monthly fee. Worth a support call, but not always available.
  • Use IPv6. If your ISP and the connecting device support it, IPv6 needs no NAT. Many home networks still lack end-to-end IPv6.
  • Rent a VPS and build a reverse tunnel. Full control, but you provision, secure, and maintain the server yourself.
  • Use an overlay networking tool like Tailscale. Great for private access between your devices, but not designed for giving the public a URL.
  • Use a managed tunneling service such as Pinggy. Your server connects outward to a public endpoint, and traffic flows back through that connection. Nothing to rent or maintain.

The last option works because CGNAT only blocks inbound connections. Outbound connections pass through, so a tunnel that starts inside your network goes straight out.

How reverse tunneling gets around CGNAT

A reverse tunnel flips connection direction. Instead of the internet trying to reach your Pi, your Pi opens an outbound SSH connection to a server with a public address. That server relays incoming requests back through the same connection.

Because the connection starts inside your network, CGNAT treats it like any other outbound traffic. Pinggy is built on this mechanism. It runs the public-facing side, so you do not have to run your own server. For more detail, read Pinggy's guide to SSH reverse tunneling.

Step-by-step: access your Raspberry Pi with Pinggy

Every modern Linux, macOS, and Windows system includes an OpenSSH client, so there is nothing to install. These steps use a Raspberry Pi, but they work for any Linux home server.

Step 1: Enable SSH on your Pi

On the Pi itself, with a keyboard and screen or over your local network, enable SSH:

sudo raspi-config

# Interface Options -> SSH -> Enable
Enter fullscreen mode Exit fullscreen mode

Before exposing anything, disable password logins and use key-based authentication. Copy your public key to the Pi first (ssh-copy-id does it in one command), or you will lock yourself out. Then edit /etc/ssh/sshd_config, set PasswordAuthentication no, and restart the service:

sudo systemctl restart ssh
Enter fullscreen mode Exit fullscreen mode

Step 2: Tunnel a web service (HTTP)

If your Pi runs a web app, dashboard, or API, run this on the Pi, replacing 8080 with the port your service uses:

ssh -p 443 -R0:localhost:8080 free.pinggy.io
Enter fullscreen mode Exit fullscreen mode

Pinggy prints a public HTTPS URL, like https://xxxx.run.pinggy-free.link. Open it from any network, including your phone on mobile data, and you will see your service. No router changes needed. The Pinggy documentation covers the same flow for different setups, and the Raspberry Pi tunnel tutorial goes into more Pi-specific services.

Step 3: Reach the Pi over SSH (TCP tunnel)

To log in to the Pi remotely, create a TCP tunnel to its SSH port (22):

ssh -p 443 -R0:localhost:22 tcp@free.pinggy.io
Enter fullscreen mode Exit fullscreen mode

Pinggy returns a hostname and port. From your laptop, connect with:

ssh -p <PORT> pi@<HOSTNAME>
Enter fullscreen mode Exit fullscreen mode

Replace pi with your Pi's username and use the hostname and port from Pinggy's output. The same approach is described in Pinggy's guide to accessing an IoT device over SSH from anywhere, which also covers other single-board computers such as Orange Pi, Banana Pi, and Jetson Nano.

Step 4 (optional): Use the Pinggy CLI

If you prefer a purpose-built client to typing SSH flags, the Pinggy CLI lets you set the tunnel type (http, tcp, tls, or udp), the local port, and the web debugger port with descriptive options. The SSH tunnel from step 3 becomes pinggy --type tcp -l 22.

Keeping the tunnel alive

A tunnel started in a terminal dies when the session closes, and the free plan has a 60-minute time limit per tunnel. Free tunnels also get a new URL each time. For a home server you want to rely on, plan for two things:

  • Auto-restart. Run the tunnel as a systemd service with Restart=always, so it reconnects after network drops or a reboot. Use ServerAliveInterval=30 in your SSH options to keep the connection healthy.
  • A stable address. Persistent URLs and custom domains are part of the Pro plan, so check current Pinggy plans if you need a permanent address.

A minimal service file looks like this. Use the exact command that works in your terminal:

[Unit]
Description=Pinggy tunnel to local SSH
After=network-online.target
Wants=network-online.target

[Service]
User=pi
ExecStart=/usr/bin/ssh -p 443 -R0:localhost:22 -o ServerAliveInterval=30 -o ExitOnForwardFailure=yes YOUR_TOKEN+tcp@free.pinggy.io
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target
Enter fullscreen mode Exit fullscreen mode

Save it as /etc/systemd/system/pinggy-tunnel.service and start it with sudo systemctl enable --now pinggy-tunnel.

Security checklist for anything you expose

Making a device reachable from anywhere also makes it a target. Before you rely on a tunnel:

  • Use SSH keys, not passwords, and disable root login.
  • Add authentication to web services. Put basic auth or a login in front of dashboards and admin panels. Pinggy can add basic auth at the tunnel itself by appending -- b:user:pass to the command.
  • Use TLS tunnels when privacy matters. Pinggy's web debugger reads HTTP traffic to work. TLS tunnels keep traffic end-to-end encrypted so Pinggy cannot read it.
  • Keep software updated. Run sudo apt update && sudo apt upgrade regularly on the Pi.
  • Expose only what you need. Tunnel one service at a time and close the tunnel when done.
  • Watch the traffic. Pinggy's web debugger shows every request. The traffic inspection guide explains how to spot unexpected requests.

Troubleshooting

  • "Connection refused" on the tunnel: Make sure the local service is actually running and listening on the port you specified (ss -tlnp shows listening ports).
  • Tunnel will not start on Windows: Use 127.0.0.1 instead of localhost in the command.
  • Connection drops repeatedly: Add -o ServerAliveInterval=30 and run the tunnel under systemd so it restarts on failure.
  • Blocked on port 22: Pinggy connects over port 443, which most networks allow. Behind a strict proxy, the SSH reverse tunneling guide above shows how to route through an HTTP proxy.
  • Still unreachable: Check the tunnel type. A web app needs an HTTP tunnel, while SSH, databases, and most game servers need TCP (or UDP for games like Minecraft Bedrock).

Final thoughts

CGNAT is a limitation of your ISP's network, not a mistake in your setup, and no amount of router tweaking will fix it. Once you understand that, the fix is simple: stop waiting for the internet to reach you and connect outward instead. A reverse tunnel gives your Raspberry Pi or home server a public address without a static IP, a VPS, or router changes.

To try it, start a free tunnel from the Pinggy homepage and expose one local service to see how it works.

Reference

Top comments (0)