Your home server, NAS or development machine sits behind a router, perhaps behind carrier-grade NAT, with no public IP. frp (fast reverse proxy) solves this with a VPS you control. The VPS runs the frp server (frps) with a public IP. The machine at home runs the frp client (frpc) and keeps an outbound connection open. Traffic to the VPS is forwarded through that connection to your local service. It’s a self-hosted alternative to ngrok or Cloudflare Tunnel.

Internet ──▶ VPS (frps, public IP) ◀── outbound connection ── home machine (frpc) ──▶ localhost:8080

When to use frp

  • Self-hosting at home (Home Assistant, Jellyfin, Nextcloud) without a public IP or port forwarding.
  • Exposing a development server or webhook receiver.
  • SSH or RDP into machines behind NAT, through your own server.
  • You want no third party in the middle. Traffic passes only through your VPS.

A small VPS is plenty: frps uses very little CPU and RAM. Bandwidth through the tunnel counts against the VPS’s traffic allowance, and latency is the home → VPS → visitor path, so pick a VPS location near you and your users. See choosing a VPS location.

Download

frp is a single Go binary for each side. Get the latest release for your architecture from the project’s GitHub releases page:

wget https://github.com/fatedier/frp/releases/download/v0.61.0/frp_0.61.0_linux_amd64.tar.gz
tar xzf frp_*_linux_amd64.tar.gz && cd frp_*_linux_amd64
sudo install -m 755 frps frpc /usr/local/bin/

(Use the current version number.)

Server side (VPS): frps

/etc/frp/frps.toml:

bindPort = 7000

# HTTP(S) virtual hosts
vhostHTTPPort = 80
vhostHTTPSPort = 443

auth.method = "token"
auth.token = "a-long-random-secret"

# restrict which remote ports clients may open for TCP proxies
allowPorts = [ { start = 6000, end = 6100 } ]

# optional dashboard, bound to localhost only
webServer.addr = "127.0.0.1"
webServer.port = 7500
webServer.user = "admin"
webServer.password = "another-secret"

systemd unit:

# /etc/systemd/system/frps.service
[Unit]
Description=frp server
After=network-online.target
Wants=network-online.target

[Service]
ExecStart=/usr/local/bin/frps -c /etc/frp/frps.toml
Restart=on-failure
DynamicUser=yes
AmbientCapabilities=CAP_NET_BIND_SERVICE

[Install]
WantedBy=multi-user.target
sudo systemctl enable --now frps
sudo ufw allow 7000/tcp
sudo ufw allow 80,443/tcp
sudo ufw allow 6000:6100/tcp      # only if you use TCP proxies

Generate tokens with openssl rand -hex 32. If Nginx already uses ports 80/443 on the VPS, either let Nginx proxy to frps’s vhost ports (e.g. 8080), or run frps on a separate VPS.

Client side (home machine): frpc

/etc/frp/frpc.toml:

serverAddr = "203.0.113.10"     # your VPS
serverPort = 7000
auth.method = "token"
auth.token = "a-long-random-secret"
transport.tls.enable = true

[[proxies]]
name = "homeassistant"
type = "http"
localIP = "127.0.0.1"
localPort = 8123
customDomains = ["ha.example.com"]

[[proxies]]
name = "home-ssh"
type = "tcp"
localIP = "127.0.0.1"
localPort = 22
remotePort = 6022

Point the DNS A record for ha.example.com at the VPS IP. Run frpc as a systemd service, like frps. Then:

  • http://ha.example.com reaches Home Assistant at home;
  • ssh -p 6022 [email protected] reaches the home machine’s SSH.

HTTPS for your services

Two common approaches:

  1. Terminate TLS on the VPS. Run Nginx or Caddy on the VPS with Let’s Encrypt certificates, and proxy to frps’s HTTP vhost port. This gives automatic certificates and central logging. See Caddy reverse proxy.
  2. Use the https proxy type with TLS terminated at home (the certificate lives on the home machine). frps routes by SNI.

Also keep transport.tls.enable = true (the default in recent versions), so the frpc ↔ frps control and data channel is encrypted, too.

Security

You’re publishing home services on the internet. Treat them like any public server:

  • Strong auth.token, and TLS between client and server.
  • Limit allowPorts. Don’t let clients open arbitrary ports on your VPS.
  • Protect apps with authentication. For admin tools, add basic auth or SSO on the VPS proxy, or use frp’s stcp/xtcp (secret) types, which require a visitor-side frpc with the shared key and aren’t publicly exposed at all.
  • Keep frp and the exposed apps updated.
  • Don’t expose SSH with password authentication. Use keys. See key-only SSH.

frp vs alternatives

Tool Infrastructure Notes
frp Your own VPS Full control, any TCP/UDP/HTTP
Cloudflare Tunnel Cloudflare No VPS needed, adds WAF and Access. See Cloudflare Tunnel
Tailscale / WireGuard Mesh / your VPS Private access rather than public exposure. See WireGuard
ngrok and similar Hosted service Quickest for development. See ngrok alternatives
SSH reverse tunnel Any SSH server ssh -R, simple for one port. See SSH tunnels

Quick reference

Task Command / setting
Server config /etc/frp/frps.toml (bindPort, auth.token)
Client config /etc/frp/frpc.toml (serverAddr, [[proxies]])
HTTP by domain type = "http", customDomains
Raw TCP type = "tcp", remotePort
Private only type = "stcp" + visitor
Logs journalctl -u frps / -u frpc

Related: port forwarding explained and NAT explained.

  • Networking
  • Self-Hosting
  • Tunnels