DEV Community

Devi D.
Devi D.

Posted on

Hosting from Home Without Exposing Your IP (4 Real Methods, 2026)

How to Expose Your Home Server to the Surface Web (Without Opening Ports or Using Tor)

If you've asked "can I use Tor to publish my home server as a regular .com website," you've already found the right problem — you want your home server reachable from the internet, without exposing your home IP, without dealing with your ISP's NAT or CGNAT, and without paying for a VPS.

The answer isn't Tor. Tor exit nodes are for anonymous browsing, not for hosting. What you actually want is a tunnel — a persistent outbound connection from your server to a public endpoint, which forwards traffic back in. Your server initiates the connection, so no inbound ports need to be open, and your home IP stays hidden.

This article covers four approaches, from the simplest to the most controlled, with real configuration examples for each.


The core problem: why your home server isn't reachable

Before picking a solution, it helps to understand exactly what's blocking you.

NAT (Network Address Translation) — your router has one public IP, and all devices on your home network share it. Incoming connections have no way to know which internal device to reach unless you manually configure port forwarding.

CGNAT (Carrier-Grade NAT) — many ISPs, especially on mobile and some residential plans, put you behind a second layer of NAT at the ISP level. Even if you configure port forwarding on your router, you still don't have a routable public IP. There's nothing to forward to.

Dynamic IP — even if you have a real public IP, it changes. You can work around this with dynamic DNS (DDNS), but it's another moving part.

Port 80/443 blocking — some ISPs block inbound connections on ports 80 and 443 for residential accounts. You can change the port, but then visitors need to type yoursite.com:8080, which isn't practical.

Tunnels solve all of these simultaneously. Your server makes an outbound connection to a relay node. Traffic destined for your domain hits the relay, gets forwarded through the tunnel to your server, your server responds, and the response goes back the same way. No inbound ports. No public IP requirement. No CGNAT problem.


Option 1: Cloudflare Tunnel (easiest, free)

Cloudflare Tunnel (formerly Argo Tunnel) is the lowest-friction option. It's free, requires no open ports, and handles HTTPS automatically.

Requirements: A domain whose DNS is managed by Cloudflare (free plan works).

Install cloudflared on your server:

# Debian/Ubuntu
curl -L https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb -o cloudflared.deb
sudo dpkg -i cloudflared.deb

# Or via package manager
wget -q https://pkg.cloudflare.com/cloudflare-main.gpg -O /usr/share/keyrings/cloudflare-main.gpg
echo 'deb [signed-by=/usr/share/keyrings/cloudflare-main.gpg] https://pkg.cloudflare.com/cloudflared any main' \
  | sudo tee /etc/apt/sources.list.d/cloudflared.list
sudo apt update && sudo apt install cloudflared
Enter fullscreen mode Exit fullscreen mode

Authenticate and create a tunnel:

cloudflared tunnel login
# Opens browser for Cloudflare auth

cloudflared tunnel create my-home-server
# Creates tunnel, outputs a UUID like: a1b2c3d4-...

cloudflared tunnel route dns my-home-server yoursite.com
# Creates CNAME record in Cloudflare DNS
Enter fullscreen mode Exit fullscreen mode

Configure the tunnel (~/.cloudflared/config.yml):

tunnel: a1b2c3d4-xxxx-xxxx-xxxx-xxxxxxxxxxxx
credentials-file: /home/user/.cloudflared/a1b2c3d4-xxxx.json

ingress:
  - hostname: yoursite.com
    service: http://localhost:80
  - hostname: www.yoursite.com
    service: http://localhost:80
  - service: http_status:404
Enter fullscreen mode Exit fullscreen mode

Run as a system service:

sudo cloudflared service install
sudo systemctl enable cloudflared
sudo systemctl start cloudflared
Enter fullscreen mode Exit fullscreen mode

Your site is now live at yoursite.com. Cloudflare handles TLS termination, so your local server can run plain HTTP on port 80 internally while visitors get HTTPS externally.

Trade-offs: Cloudflare sees all your traffic (they're the relay). For most personal projects this is fine. For privacy-sensitive deployments, see options 3 and 4.


Option 2: Nginx + a lightweight VPS as relay

If you want more control — or if Cloudflare's terms don't suit your use case — a small VPS acting as a relay is the next step. You run an SSH tunnel or WireGuard between your VPS and home server, then use Nginx on the VPS to proxy traffic to your home server.

The architecture:

Visitor → VPS (yoursite.com, public IP) → SSH/WireGuard tunnel → Home server (nginx/apache)
Enter fullscreen mode Exit fullscreen mode

On the VPS — set up a persistent reverse SSH tunnel:

Your home server initiates an outbound SSH connection to the VPS, forwarding a port:

# On home server — forward local port 8080 to VPS port 9000
ssh -N -R 9000:localhost:8080 user@your-vps-ip

# Make it persistent with autossh
sudo apt install autossh
autossh -M 0 -N -R 9000:localhost:8080 user@your-vps-ip \
  -o "ServerAliveInterval 30" \
  -o "ServerAliveCountMax 3"
Enter fullscreen mode Exit fullscreen mode

Systemd service for autossh (/etc/systemd/system/tunnel.service):

[Unit]
Description=Reverse SSH tunnel to VPS
After=network.target

[Service]
User=youruser
ExecStart=/usr/bin/autossh -M 0 -N \
  -R 9000:localhost:8080 user@your-vps-ip \
  -o "ServerAliveInterval 30" \
  -o "ServerAliveCountMax 3" \
  -i /home/youruser/.ssh/id_rsa
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target
Enter fullscreen mode Exit fullscreen mode
sudo systemctl enable tunnel
sudo systemctl start tunnel
Enter fullscreen mode Exit fullscreen mode

On the VPS — Nginx reverse proxy:

server {
    listen 80;
    server_name yoursite.com www.yoursite.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    server_name yoursite.com www.yoursite.com;

    ssl_certificate /etc/letsencrypt/live/yoursite.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/yoursite.com/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:9000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}
Enter fullscreen mode Exit fullscreen mode

Get a free TLS cert on the VPS:

sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d yoursite.com -d www.yoursite.com
Enter fullscreen mode Exit fullscreen mode

For the VPS itself, you want something minimal — the relay only needs to forward traffic, not run your application. A 1-core/512MB instance is sufficient. vpso.cc has VPS plans that work for this role; the key requirement is a static public IP and reliable uptime, which is standard across most providers.


Option 3: WireGuard tunnel (faster, more private)

SSH tunneling works but has overhead. WireGuard is a modern VPN protocol that's faster, more efficient, and cleaner to configure. Same architecture as Option 2, but the tunnel is WireGuard instead of SSH.

On the VPS — install and configure WireGuard:

sudo apt install wireguard

# Generate keys on VPS
wg genkey | tee /etc/wireguard/privatekey | wg pubkey > /etc/wireguard/publickey
Enter fullscreen mode Exit fullscreen mode

VPS WireGuard config (/etc/wireguard/wg0.conf):

[Interface]
Address = 10.0.0.1/24
ListenPort = 51820
PrivateKey = <VPS_PRIVATE_KEY>

[Peer]
PublicKey = <HOME_SERVER_PUBLIC_KEY>
AllowedIPs = 10.0.0.2/32
Enter fullscreen mode Exit fullscreen mode

Home server WireGuard config (/etc/wireguard/wg0.conf):

[Interface]
Address = 10.0.0.2/24
PrivateKey = <HOME_SERVER_PRIVATE_KEY>

[Peer]
PublicKey = <VPS_PUBLIC_KEY>
Endpoint = your-vps-ip:51820
AllowedIPs = 10.0.0.1/32
PersistentKeepalive = 25
Enter fullscreen mode Exit fullscreen mode
# Enable on both machines
sudo systemctl enable wg-quick@wg0
sudo systemctl start wg-quick@wg0
Enter fullscreen mode Exit fullscreen mode

Then update your Nginx config on the VPS to proxy to 10.0.0.2:80 (your home server's WireGuard IP) instead of localhost:9000. Traffic flows through the encrypted WireGuard tunnel rather than an SSH connection.


Option 4: ngrok (easiest for testing, not production)

ngrok is useful for development and testing — exposing a local server quickly without any configuration. Not recommended for production because the free tier gives you a random subdomain that changes on restart, and paid tiers are relatively expensive for persistent use.

# Install
curl -s https://ngrok-agent.s3.amazonaws.com/ngrok.asc | sudo tee /etc/apt/trusted.gpg.d/ngrok.asc >/dev/null
echo "deb https://ngrok-agent.s3.amazonaws.com buster main" | sudo tee /etc/apt/sources.list.d/ngrok.list
sudo apt update && sudo apt install ngrok

# Authenticate
ngrok config add-authtoken YOUR_TOKEN

# Expose local port 80
ngrok http 80
Enter fullscreen mode Exit fullscreen mode

You get a public HTTPS URL immediately. Good for showing someone a local project, not for running a real site.


Choosing the right option

Cost Control Privacy Setup complexity
Cloudflare Tunnel Free Low (CF sees traffic) Cloudflare-level Low
SSH reverse tunnel + VPS VPS cost Full Good Medium
WireGuard + VPS VPS cost Full Best Medium
ngrok Free/paid Low Moderate Very low

For most home server setups: Cloudflare Tunnel if you don't mind Cloudflare seeing your traffic and you want zero-maintenance. WireGuard + VPS if you want full control and better privacy — the VPS cost is typically $3–6/month for a relay-only instance.


What about Tor?

Since this comes up: you can run a Tor hidden service and also expose it to the surface web via a Tor2Web proxy, but this is not recommended for a production site. The latency is significant (300ms+ added per hop), Tor2Web proxies are run by third parties you don't control, and the setup gives you the downsides of both systems without the full benefits of either.

The tunnel approaches above are faster, more reliable, and easier to maintain. Tor makes sense for anonymity-critical deployments. For a regular home-hosted website, it's the wrong layer to solve this at.


Security considerations

A few things worth addressing before you go live:

Rate limiting on the VPS (Nginx):

# Add to nginx.conf http block
limit_req_zone $binary_remote_addr zone=one:10m rate=10r/s;

# Add to server block
limit_req zone=one burst=20 nodelay;
Enter fullscreen mode Exit fullscreen mode

Fail2ban on the VPS to block repeated bad requests:

sudo apt install fail2ban
sudo systemctl enable fail2ban
Enter fullscreen mode Exit fullscreen mode

Keep your home server's SSH port closed to the VPS inbound — the tunnel is initiated from your home server outbound, so no inbound SSH from the VPS to home is needed. On your VPS, allow only ports 80, 443, and the WireGuard port (51820):

sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow 51820/udp
sudo ufw enable
Enter fullscreen mode Exit fullscreen mode

Your home IP remains hidden as long as your application doesn't leak it (check headers, error pages, and any user-generated content that might reference internal IPs).

Top comments (1)

Collapse
 
szp2005 profile image
szp2005

For the VPS options, worth noting the relay IP is datacenter space, and plenty of reputation feeds flag entire hosting ranges as proxy by default. Grading the /24 tells you more than checking the single address, since a block where 30% of neighbours are already flagged drags yours down with it. Cheap to check before you point DNS at it.