Introduction
frp is the most popular open-source reverse proxy for exposing services behind NAT or firewalls. Run frps on a public server with an open port; run frpc on your home server or laptop; frpc tunnels back to frps. Any public request hitting frps gets relayed through the tunnel to your private service.
With over 105,000 GitHub stars (top-20 in all of GitHub), frp is a workhorse for remote development, home-lab exposure, webhook testing, and self-hosted alternatives to ngrok. It supports HTTP, HTTPS, TCP, UDP, and plugins for additional protocols.
What frp Does
frp has two components: frps (server, runs on a VPS with public IP) and frpc (client, runs wherever the service lives). They establish a persistent TCP connection; frps listens for incoming traffic and forwards it down the tunnel to frpc, which hands it to the local service. The client config declares proxies (local_port -> public_port or domain).
Architecture Overview
[Internet Client] --http(s)--> [frps: VPS:80/443] <--tunnel-- [frpc: home server] --> [local app :3000]
|
[local app :22 (ssh)]
|
[local app :5432 (pg)]
Supported proxy types:
http, https (with TLS termination on frps)
tcp, tcpmux (shared port)
udp
stcp, sudp (encrypted p2p via frps as rendezvous)
xtcp (NAT hole punching for direct p2p)Self-Hosting & Configuration
# frps.toml — full config
bindPort = 7000
vhostHTTPPort = 80
vhostHTTPSPort = 443
tlsOnly = false
auth.method = "token"
auth.token = "some-random-secret"
subDomainHost = "frp.example.com" # enables subdomain routing
webServer.addr = "0.0.0.0"
webServer.port = 7500
webServer.user = "admin"
webServer.password = "admin-pass"
# frpc.toml — multiple services at once
serverAddr = "frp.example.com"
serverPort = 7000
auth.token = "some-random-secret"
[[proxies]]
name = "web"
type = "http"
localPort = 3000
subdomain = "dev" # -> https://dev.frp.example.com
[[proxies]]
name = "ssh"
type = "tcp"
localIP = "127.0.0.1"
localPort = 22
remotePort = 6000 # -> ssh user@frp.example.com -p 6000
[[proxies]]
name = "plex"
type = "https"
localPort = 32400
customDomains = ["plex.example.com"]Key Features
- HTTP / HTTPS / TCP / UDP / XTCP — cover any protocol
- Subdomain routing — dev.frp.example.com, app.frp.example.com auto-routed
- Custom domains + SSL — Let's Encrypt or your own certs on frps
- Authentication — token or OIDC-based auth
- Web dashboard — admin UI for active tunnels + traffic
- Plugins — HTTP-basic-auth, rewrite, rate limit, transparent proxy
- NAT traversal (xtcp) — peer-to-peer connections without relay bandwidth
- systemd + Docker friendly — standard deployment patterns
Comparison with Similar Tools
| Feature | frp | ngrok | Cloudflare Tunnel | Tailscale Funnel | bore |
|---|---|---|---|---|---|
| Self-hosted | Yes | Paid tier only | Paid (but free tunnel) | Via Tailscale | Yes |
| Custom domain | Yes | Paid | Yes | Limited | Yes |
| TCP/UDP | Yes | Paid | Limited | Limited | TCP only |
| Setup | Moderate | Very low | Low | Low | Very low |
| Cost | Free (your VPS) | Free + paid tiers | Free (for simple cases) | Free (20 devices) | Free |
| Dashboard | Yes | Yes | Yes | Yes | No |
| Best For | Full control / many tunnels | Quick dev tunnels | HTTP apps on Cloudflare | Already on Tailscale | Minimal CLI tunnel |
FAQ
Q: frp vs ngrok? A: Use ngrok for 2-minute dev tunnels; use frp when you want a permanent, self-owned tunnel infrastructure and custom domains without a subscription.
Q: Can I use frp for gaming/low-latency? A: For the lowest latency, xtcp/stcp (p2p modes) beat the relay. They require that both sides can punch through NAT (many home routers can).
Q: Is it secure? A: With a strong token and HTTPS, yes. For zero-trust, put frp behind an auth proxy (Authelia, Authentik) and never expose services unauthenticated.
Q: Can I expose a database safely?
A: Possible but risky. Use SSH tunneling via type = "stcp" so only authorized clients can connect — no port is publicly exposed.
Sources
- GitHub: https://github.com/fatedier/frp
- License: Apache-2.0