VPN vs Reverse Proxy: Secure Remote Access to Your Homelab

VPN vs Reverse Proxy: Secure Remote Access to Your Homelab

When I first ran services on my homelab—Nextcloud, Jellyfin, Vaultwarden—I had to choose how to reach them securely from outside my network. The answer wasn't obvious. Both VPN and reverse proxy solutions work, but they protect differently, cost differently, and scale differently. This guide walks through the real tradeoffs so you can pick the right method for your setup.

The Core Difference

A VPN encrypts all your traffic and makes your client appear as if it's inside your home network. Everything routes through the VPN server—every DNS query, every packet, every application.

A reverse proxy sits between the internet and your services, accepting incoming HTTPS requests and forwarding them to your internal apps. Only the traffic destined for those specific services goes through the proxy.

In practice, a VPN is a network-level solution. A reverse proxy is an application-level solution. The difference matters more than you'd think.

Security: Which Is Actually Safer?

I started with the assumption that VPN was "more secure" because it encrypts everything. That's partially true, but incomplete.

VPN advantages:

VPN disadvantages:

Reverse proxy advantages:

Reverse proxy disadvantages:

Tip: You don't have to choose one. Many homelabbers run both: a reverse proxy for web apps (Nextcloud, Jellyfin, Immich) and a VPN for internal resources (SSH, file shares). Caddy or Nginx handles HTTPS to the apps. WireGuard or Tailscale handles everything else.

My Reverse Proxy Setup: Caddy + Cloudflare Tunnel

For my web services, I prefer Caddy because it's simple and handles HTTPS automatically. Here's what I run:

version: '3.8'
services:
  caddy:
    image: caddy:latest
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile
      - caddy_data:/data
      - caddy_config:/config
    restart: unless-stopped

  nextcloud:
    image: nextcloud:latest
    environment:
      - MYSQL_HOST=db
      - MYSQL_USER=nextcloud
      - MYSQL_PASSWORD=secure_password
      - MYSQL_DB=nextcloud
    volumes:
      - ./nextcloud:/var/www/html
    restart: unless-stopped

  db:
    image: mariadb:latest
    environment:
      - MYSQL_ROOT_PASSWORD=root_password
      - MYSQL_DATABASE=nextcloud
      - MYSQL_USER=nextcloud
      - MYSQL_PASSWORD=secure_password
    volumes:
      - ./db:/var/lib/mysql
    restart: unless-stopped

volumes:
  caddy_data:
  caddy_config:

And the Caddyfile:

nextcloud.example.com {
    reverse_proxy nextcloud:80 {
        header_upstream X-Forwarded-For {http.request.remote}
        header_upstream X-Forwarded-Proto {http.request.proto}
    }
}

jellyfin.example.com {
    reverse_proxy jellyfin:8096
}

vaultwarden.example.com {
    reverse_proxy vaultwarden:80
}

This setup automatically fetches and renews SSL certificates. I don't expose my services to the raw internet—they hide behind Caddy. If I want to keep my homelab IP private, I use Cloudflare Tunnel (formerly Argo Tunnel) which punches through my firewall without opening ports.

Cost: $0 upfront. Just DNS setup time.

My VPN Setup: Tailscale

For actual remote access to internal resources—my SSH server, NAS, Kubernetes API—I run Tailscale. It's a managed WireGuard network that's trivially easy to set up:

curl -fsSL https://tailscale.com/install.sh | sh

# On the homelab machine
sudo tailscale up

# Visit the auth link, sign in, approve the device
# Now your homelab has a stable Tailscale IP (e.g., 100.0.0.5)

# From your laptop (also running Tailscale)
ssh [email protected]

# Or mount the NAS
mount -t nfs 100.0.0.42:/mnt/storage /mnt/remote

Tailscale is convenient because:

Cost: Free for up to 3 devices. Paid plans start at $60/year for more.

Watch out: Tailscale is convenient, but it's a closed-source client with a Tailscale-managed control server. If you need 100% open-source for compliance or privacy, run self-hosted WireGuard instead. It's more work but gives you full control.

VPN: Self-Hosted WireGuard

If you want VPN without proprietary software, WireGuard is the gold standard. It's lightweight, audited, and fast. Here's the basic setup:

On a cheap VPS (I recommend RackNerd—their KVM VPS is excellent value), install WireGuard:

sudo apt update && sudo apt install wireguard wireguard-tools

# Generate keys
wg genkey | tee privatekey | wg pubkey > publickey
wg genkey | tee client_privatekey | wg pubkey > client_publickey

# Server config: /etc/wireguard/wg0.conf
[Interface]
Address = 10.0.0.1/24
ListenPort = 51820
PrivateKey = 

[Peer]
PublicKey = 
AllowedIPs = 10.0.0.2/32

# On your homelab:
[Interface]
Address = 10.0.0.2/24
PrivateKey = 

[Peer]
PublicKey = 
Endpoint = your-vps-ip:51820
AllowedIPs = 10.0.0.0/24

# Enable and start
sudo systemctl enable wg-quick@wg0
sudo systemctl start wg-quick@wg0

This gives you a true VPN: encrypted tunnel, full network access, zero logs (you control the server). The trade-off is you manage it yourself—patches, firewall rules, and failure recovery are your responsibility.

Performance: Real-World Latency

From my testing:

For casual browsing (Nextcloud, Jellyfin), differences are imperceptible. For heavy uploads or large file transfers, a reverse proxy is faster because it doesn't funnel all traffic through a single VPN gateway.

Cost Comparison

Method Setup Cost Monthly Cost Management
Reverse Proxy Only 1–2 hours $0 Updates to Caddy/Nginx
Tailscale VPN 10 min $0–$60/year None (managed)
Self-Hosted WireGuard 2–4 hours $3–10 (VPS) Full responsibility

Which Should You Choose?

Use a reverse proxy if:

Use a VPN (Tailscale) if:

Use self-hosted WireGuard if:

The Hybrid Approach (My Recommendation)

I run both. Caddy reverse proxy handles web services (Nextcloud, Jellyfin). Tailscale handles everything else (SSH, NFS mounts, Kubernetes API). This gives me:

If I