Hosting Websites from Home Behind CGNAT (Without a Public IP)
Hosting web services and applications on a home server provides complete control over your data, access to your own hardware, and eliminates recurring compute fees. However, modern residential internet connections present a major roadblock: Carrier-Grade NAT (CGNAT).
The Problem: Why Port Forwarding Won’t Work
With usual broadband, your ISP assigns your you a publicly routable IPv4 address. Directing web traffic to a home server was as simple as forwarding incoming requests on ports 80 (HTTP) and 443 (HTTPS) to your local server’s internal IP address.
Under CGNAT, ISPs no longer give each connection its own public IP. Instead:
-
Hundreds of subscribers share a single public IPv4 address upstream at the ISP level. You will only know what public IP address you have if you check if using services like https://icanhazip.com/
-
Your home router gets a private address (typically within the
100.64.0.0/10range) rather than a true internet-facing public IP. -
The Inbound Block: The ISP firewall blocks all inbound connection attempts originating from the public internet. When a visitor types your domain name into a browser, the ISP’s edge router drops the packets because it has no record of which home subscriber should receive them.
THE INBOUND BLOCK (Why standard hosting fails):
Public Visitor ISP Gateway (CGNAT Pool) Home Router
[ Browser: 443 ] ---> [ Request ] ---> [ Shared Public IP ] ---X DROPPED! ---> [ Private WAN ]
No inbound state entry / No way to route
Port forwarding broken to home server
This makes sense because 99% of normal broadband users would never need to allow incoming traffic to their home. Why waste precious public IP addresses when its not going to be used by 99% of users.
Outbound Connections Always Work
While CGNAT strictly rejects incoming connection requests, it freely permits outgoing traffic.
When your home server reaches out to an external server on the internet, your ISP’s router creates a temporary entry in its state table to track that session. Traffic returning along that already-established channel passes through without restriction.
The entire solution takes advantage of this property: instead of waiting for the internet to connect inward, your home server initiates an encrypted, persistent outbound tunnel to a cloud relay.
The Architecture: How My Workaround Works
This setup places a lightweight cloud server (Oracle Cloud VPS in my case) on the Internet, establishes an encrypted WireGuard mesh tunnel (Tailscale) back to your home hypervisor (Proxmox VE), and secures the entry point using Caddy Reverse Proxy.
+-----------------------------------------------------------------------------------------+
| THE PUBLIC INTERNET |
| |
| [ Public Visitor ] |
| | |
| | (1) HTTPS Request (e.g., https://example.com) |
| v |
| |
+---------------|-------------------------------------------------------------------------+
|
|
v
+-----------------------------------------------------------------------------------------+
| ORACLE CLOUD INFRASTRUCTURE (VPS) |
| |
| [ Caddy Reverse Proxy ] |
| - Public gateway listening on Ports 80 & 443 |
| - Automatically provisions & renews SSL/TLS certificates |
| - Inspects incoming domain host headers |
| | |
| | (2) Routes internally into private mesh network |
| v |
| [ Tailscale Node: VPS ] (IP: 100.x.y.1) |
+---------------|-------------------------------------------------------------------------+
|
| ==================================================================================
| ENCRYPTED WIREGUARD TUNNEL (Outbound-Initiated by Tailscale running on Proxmox) |
| =================================================================================
|
| * Bypasses ISP CGNAT seamlessly because the session was |
| established from the INSIDE going out. |
| * Requires ZERO open ports or port-forwarding on home router. |
v
+-----------------------------------------------------------------------------------------+
| HOME NETWORK (PROXMOX VE HYPERVISOR) |
| |
| [ Tailscale Node: Proxmox ] (IP: 100.x.y.2) |
| - Decrypts traffic received from VPS node |
| | |
| | (3) Local bridge / NAT handoff |
| v |
| [ Proxmox LXC Container ] (IP: 10.10.0.14) |
| - Runs your web application / website |
| - Processes request & sends response back through the tunnel |
+-----------------------------------------------------------------------------------------+
Step-by-Step Traffic Lifecycle
-
Edge Entry (Cloudflare): A visitor navigates to your domain. Cloudflare resolves the DNS query to VPS IP address
-
Cloud Ingress (Oracle VPS & Caddy): The request lands on your Oracle Cloud VPS over standard web ports (80,443). Caddy terminates the TLS connection, validates the domain, and acts as a reverse proxy.
-
Tunnel Ingress (Tailscale): Instead of pointing to a public IP, Caddy forwards the traffic to an internal, virtual Tailscale IP (
100.x.y.z) assigned to your Proxmox Server. -
NAT Traversal: Because your Proxmox server maintains an active, outbound connection to the Tailscale mesh, the packets traverse through your ISP’s CGNAT barriers without triggering firewall drops.
-
Local Routing (Proxmox to LXC): Proxmox receives the tunnel traffic on its virtual interface and hands it off directly to the designated Linux Container (LXC) hosting the web application.
-
Return Route: The web app generates the response, sends it back over the WireGuard tunnel to Caddy, and Caddy returns it to the user’s browser.
Why This Design Is Resilient
-
No Open Ports on Home Router: Your home network remains completely closed to arbitrary scans, bots, and scrapers.
-
IP Independence: If your ISP reconnects, reboots, or changes your home network’s assigned IP block, Tailscale renegotiates the tunnel immediately with zero downtime or manual DNS re-pointing.
-
Total Privacy: Your home’s physical location and residential ISP details are hidden behind the VPS.
-
Clean Separation of Concerns: Compute and storage stay locally on your own Proxmox hardware, while network ingress is delegated to a resilient cloud front door.