expose is a self-hosted HTTP tunnel. Run your own server, then expose local HTTP ports from any machine - no third-party services required.
- HTTPS-only public traffic with automatic TLS (ACME) or static wildcard certificates
- Built-in WAF blocks SQL injection, XSS, path traversal, and other attacks before they reach your app
- Multi-route configs via
expose.yml- expose multiple services under one subdomain with path-based routing - Real-time client dashboard with request log, latency percentiles, WAF counters, and connection stats
- Auto-update - server and client can self-update in the background with zero downtime
- Password-protected tunnels with HTTP Basic Auth (per-tunnel or per-config)
- Env-first configuration - minimal CLI flags,
.envsupport, and interactive setup wizards - Rate limiting on tunnel registration to prevent abuse
- Persistent login - authenticate once with
expose login, credentials are saved locally - Automatic reconnection with exponential backoff and keepalive pings
flowchart TB
App["π» Local app<br/>127.0.0.1:PORT"]
subgraph client["expose client"]
Fwd["Forward"] --> Conn["Connect"] --> Reg["Register"]
end
subgraph server["expose server"]
Hub{{"Session hub"}}
Route["Route by hostname"]
TLS["TLS Β· WAF"]
DB[("SQLite")]
Hub --> Route --> TLS
Route -. resolve .-> DB
end
Browser["π Browser"]
App -- "HTTP" --> Fwd
Hub <-- "WebSocket tunnel" --> Conn
Conn -- "token" --> Hub
Reg -- "API key" --> Route
TLS -- "HTTPS *.domain" --> Browser
- The server terminates TLS, runs WAF inspection, and routes requests by hostname to the correct tunnel
- The client registers via API key, opens a persistent WebSocket, and proxies requests to your local port
- Requests and responses flow over the WebSocket as JSON envelopes with binary streaming for large bodies
For the full request lifecycle and component breakdown, see Architecture Overview.
- A server or VPS with a public IP - or a home server with port forwarding configured
- A domain you control (e.g.
example.com) - A DNS wildcard A record (
*.example.com) pointing to your server's public IP
Download the latest binary from Releases and place it in your PATH.
On your public-facing machine, run the interactive setup (writes a .env for you):
expose server init # guided setup
expose server # start the tunnel serverThen create an API key for your client(s):
expose apikey create --name defaultOn any machine you want to expose:
expose login
expose http 3000Open the URL shown in the terminal - that's it.
For the full walkthrough, DNS setup guides, and multi-route configs, see Quick Start.
See the docs/ folder for all guides - server & client configuration, TLS modes, DNS setup, deployment, WAF, auto-update, troubleshooting, and more.
- ngrok - the gold standard for HTTP tunnels and a huge inspiration.
exposeexists because I needed more freedom and control over infrastructure, but ngrok paved the way. - OpenAI and Anthropic - AI-assisted development boosted the entire build lifecycle by 10Γ.
MIT
