How to Expose Self-Hosted Services to the Internet

What Is This Guide?

You’ve set up self-hosted services at home. Now you want to access them from outside your network — from your phone, your office, or anywhere on the internet. This guide covers every method for exposing self-hosted services, ranked by security, complexity, and cost.

Prerequisites

  • A running self-hosted service you want to access remotely
  • A domain name (recommended but not required for all methods)
  • Basic understanding of networking concepts
  • A reverse proxy (for public-facing methods)

The Four Approaches

MethodSecurityComplexityCostBest For
Cloudflare TunnelHighLowFreePublic services (websites, APIs)
VPN (Tailscale/WireGuard)Very HighMediumFreePersonal access to all services
Port Forwarding + Reverse ProxyMediumMediumFreeFull control, no third parties
VPS Reverse ProxyHighHigh$5-12/moStatic IP + privacy

Cloudflare Tunnel creates an outbound-only connection from your server to Cloudflare’s network. No open ports, no port forwarding, no exposed IP address. Traffic flows: Internet → Cloudflare → Tunnel → Your server.

Setup

  1. Add your domain to Cloudflare (free plan works)
  2. Install cloudflared on your server:
curl -L --output cloudflared.deb https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb
sudo dpkg -i cloudflared.deb
  1. Authenticate and create a tunnel:
cloudflared tunnel login
cloudflared tunnel create my-tunnel
  1. Configure the tunnel (~/.cloudflared/config.yml):
tunnel: <tunnel-id>
credentials-file: /root/.cloudflared/<tunnel-id>.json

ingress:
  - hostname: nextcloud.yourdomain.com
    service: http://localhost:8080
  - hostname: jellyfin.yourdomain.com
    service: http://localhost:8096
  - service: http_status:404
  1. Route DNS and run:
cloudflared tunnel route dns my-tunnel nextcloud.yourdomain.com
cloudflared tunnel route dns my-tunnel jellyfin.yourdomain.com
cloudflared tunnel run my-tunnel

Or run via Docker Compose:

services:
  cloudflared:
    image: cloudflare/cloudflared:2024.12.2
    container_name: cloudflared
    restart: unless-stopped
    command: tunnel run
    environment:
      TUNNEL_TOKEN: ${CLOUDFLARE_TUNNEL_TOKEN}

Pros and Cons

ProsCons
No open portsCloudflare sees all traffic (no E2E encryption)
Free SSL certificatesSome protocols not supported (raw TCP limited)
DDoS protection includedCloudflare TOS prohibits streaming video through free tunnels
No static IP neededAdds latency (traffic goes through Cloudflare edge)
Zero-trust access policiesVendor lock-in

Best for: Websites, APIs, web apps you want publicly accessible. Not for Jellyfin/Plex streaming (TOS issue) or gaming servers (UDP performance).

See our full guide: How to Set Up Cloudflare Tunnel

A VPN gives you a private encrypted tunnel to your home network. All your services remain unexposed to the internet — you access them as if you were on your local network.

Option A: Tailscale / Headscale (Easiest)

Tailscale is a mesh VPN built on WireGuard. Install the client on your server and your devices — they connect directly, peer-to-peer. Headscale is the self-hosted control server.

# On your server
curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up

# On your phone/laptop — install the Tailscale app
# Both devices now see each other on the Tailscale network

Access your services using the Tailscale IP: http://100.x.x.x:8080

Option B: WireGuard (Most Control)

WireGuard is a kernel-level VPN. Faster than Tailscale (no control plane overhead) but requires manual key management.

services:
  wireguard:
    image: lscr.io/linuxserver/wireguard:1.0.20210914
    container_name: wireguard
    restart: unless-stopped
    cap_add:
      - NET_ADMIN
      - SYS_MODULE
    environment:
      PUID: 1000
      PGID: 1000
      TZ: UTC
      SERVERURL: your-public-ip-or-ddns
      SERVERPORT: 51820
      PEERS: phone,laptop,tablet
      PEERDNS: auto
    ports:
      - 51820:51820/udp
    volumes:
      - ./wireguard-config:/config
    sysctls:
      - net.ipv4.conf.all.src_valid_mark=1

This generates QR codes for each peer in ./wireguard-config/peer_*/. Scan with the WireGuard mobile app.

VPN Comparison

FeatureTailscaleHeadscaleWireGuard
Setup time2 minutes15 minutes30 minutes
Control planeTailscale cloudSelf-hostedManual
NAT traversalAutomatic (DERP relays)AutomaticManual (port forward needed)
Key managementAutomaticAutomaticManual
Max devices (free)100UnlimitedUnlimited
SpeedFastFastFastest
Requires open portNoNoYes (UDP 51820)

Best for: Accessing all your services privately from any device. Tailscale for ease, WireGuard for performance and full control.

See our guides: WireGuard Setup | Headscale Setup | Tailscale vs WireGuard

Method 3: Port Forwarding + Reverse Proxy

The traditional approach: forward ports on your router to your server, and use a reverse proxy with SSL to serve your applications.

Setup

  1. Get a domain and point it to your public IP (use DDNS if your IP is dynamic)
  2. Forward ports on your router:
    • Port 80 → your server IP:80 (HTTP, for SSL certificate challenges)
    • Port 443 → your server IP:443 (HTTPS)
  3. Set up a reverse proxy (Nginx Proxy Manager, Caddy, or Traefik)
  4. Get SSL certificates — Let’s Encrypt via your reverse proxy
  5. Add proxy hosts for each service

Security Hardening (Required)

Port forwarding exposes your home IP address and server directly to the internet. You must harden:

# Enable firewall — only allow SSH, HTTP, HTTPS
sudo ufw default deny incoming
sudo ufw allow ssh
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable

# Install fail2ban
sudo apt install fail2ban
sudo systemctl enable fail2ban

# Keep system updated
sudo apt update && sudo apt upgrade -y

Pros and Cons

ProsCons
Full control, no third partiesExposes your home IP
No bandwidth limitsRequires static IP or DDNS
Lowest latencyYou manage security entirely
Works with any protocolISP may block ports 80/443

Best for: Users who want full control and have a static IP or reliable DDNS. Requires strong security practices.

Method 4: VPS Reverse Proxy

Rent a cheap VPS, run your reverse proxy on it, and tunnel traffic back to your home server via WireGuard. Your home IP stays hidden, you get a static IP, and the VPS handles SSL.

Internet → VPS (public IP) → WireGuard tunnel → Home server

Setup Overview

  1. Rent a VPS ($5-12/month — Hetzner, DigitalOcean, Linode)
  2. Install WireGuard on both VPS and home server
  3. Install a reverse proxy on the VPS (Caddy or Nginx)
  4. Proxy requests through the WireGuard tunnel to your home server’s WireGuard IP

This combines the best of both worlds: public accessibility without exposing your home IP, and no third-party middleman seeing your traffic.

Best for: Privacy-conscious users who need public services but don’t want to expose their home IP or use Cloudflare.

Decision Guide

Do you need PUBLIC access (anyone on the internet)?
├── Yes → Is it a website or web app?
│   ├── Yes → Cloudflare Tunnel (easiest, free)
│   └── No (gaming, streaming, raw TCP) → Port forwarding or VPS reverse proxy
└── No (just you and your devices) → VPN
    ├── Want easiest setup? → Tailscale
    ├── Want self-hosted control? → Headscale
    └── Want maximum performance? → WireGuard

Common Mistakes

  1. Exposing services without SSL — Never run HTTP over the public internet. Use HTTPS via Let’s Encrypt.
  2. Using port forwarding for everything — If only you need access, use a VPN. Port forwarding exposes attack surface unnecessarily.
  3. Forgetting DDNS — If your ISP assigns dynamic IPs, your domain stops working when the IP changes. Use DDNS.
  4. No firewall on port-forwarded servers — If you forward ports, run ufw or iptables and fail2ban. No exceptions.
  5. Cloudflare Tunnel for streaming — Cloudflare’s free plan TOS prohibits serving large media files. Use a VPN or port forwarding for Jellyfin/Plex.

Next Steps

Comments