Documentation
Everything you need to expose localhost to the internet — install, authenticate, and tunnel in under a minute.
Introduction
poriq is an open-source tunneling tool that gives any local service a public HTTPS URL. Point it at a port and you get a shareable https://yourapp.poriq.run address with automatic TLS — no firewall changes, no deploy. It speaks HTTP, TCP, and UDP, and it is MIT-licensed and self-hostable.
A tunnel runs over a single persistent WebSocket between the CLI and the server, so connections survive flaky networks and reconnect on their own. Large uploads stream rather than buffer, and inner WebSockets (like a dev server's HMR socket) are proxied transparently.
Installation
Install with Go (1.22+):
go install github.com/poriq/poriq@latest
This drops a poriq binary in your $GOPATH/bin. Confirm it is on your PATH:
poriq version
Authentication
Sign in once with your GitHub account. This opens a browser device flow and stores an API token locally (~/.config/poriq/config.json).
poriq login # → opens GitHub, enter the shown code # → "logged in as <you>"
Check status or sign out at any time:
poriq status poriq logout
Quick Start
Expose a local server running on port 3000:
poriq http 3000 Tunnel established! Public URL: https://bold-wren-204.poriq.run Forwarding: https://bold-wren-204.poriq.run → http://localhost:3000 Inspector: http://localhost:4040
Share the public URL. Every request is forwarded to your local server and shown live in the terminal and the inspector.
HTTP Tunnels
poriq http is the workhorse. Pass one or more ports — multiple ports open multiple tunnels at once.
poriq http 3000 # single tunnel poriq http 3000 8080 # two tunnels poriq http https://localhost:8443 # HTTPS local target (cert ignored)
Options
| <port[:subdomain]> | Local port to expose. Optionally pin a subdomain, e.g. 3000:myapp. |
| --new | Request a fresh random subdomain instead of reusing a saved one. |
| --inspect <port> | Web inspector UI port (default 4040, 0 to disable). |
| --log <file> | Append every request to a JSONL file for later replay. |
| --auth <user:pass> | Protect the tunnel with HTTP Basic auth. |
| --allow-ip <list> | Allow only these IPs/CIDRs (comma-separated). |
| --rate-limit <rps> | Cap requests per second to your local server. |
| --rate-limit-path <path:rps> | Per-path rate limit override (repeatable). |
| --mock <path:status:body> | Return a canned response for a path (repeatable). |
| --rewrite <from:to> | Rewrite a request path prefix before forwarding (repeatable). |
| --header-add / --header-remove | Add or strip request headers before forwarding. |
| --response-header-add / -remove | Add or strip headers on the response. |
| --wol <name=MAC|IPv4> | Register a Wake-on-LAN target (repeatable). |
| --notify-on-error | Desktop notification on 5xx responses. |
| --timeout <dur> | Local response timeout, e.g. 30s, 1m. |
| --drain-timeout <dur> | Grace period for in-flight requests on shutdown (default 5s). |
| --quiet | Suppress the per-request log output. |
TCP & UDP Tunnels
Forward raw TCP or UDP — SSH, databases, game servers, anything. These are available on paid plans and expose a host:port instead of an HTTPS URL.
poriq tcp 22 # → tunnel.poriq.run:10000 → localhost:22 poriq udp 51820 # WireGuard, DNS, game servers, …
Connect using the host and allocated port poriq prints on start.
WebSockets & HMR
WebSocket upgrades are proxied bidirectionally over the same tunnel, so realtime apps and dev-server hot reload work out of the box.
One caveat for framework dev servers (Next.js, Vite, …): they reject cross-origin dev requests by default. Add your tunnel host to the dev-origin allowlist, e.g. in next.config.js:
// next.config.js
module.exports = {
allowedDevOrigins: ["*.poriq.run"],
};Custom Subdomains
Free tunnels get a random subdomain each session. Paid plans can reserve subdomains and reuse them. Pin one inline:
poriq http 3000:myapp # → https://myapp.poriq.run
poriq remembers the subdomain per local URL, so the next run reuses it automatically. Pass --new to force a fresh one. Reserve and manage subdomains from the dashboard.
Request Inspector
Every HTTP tunnel ships a local web inspector (default http://localhost:4040) showing each request and response — headers, body, status, and timing.
poriq http 3000 --inspect 4040 # change the port poriq http 3000 --inspect 0 # disable it
Log requests to a file and replay them later in the same UI:
poriq http 3000 --log requests.jsonl poriq inspect requests.jsonl # open the saved log
Config File
Drop a poriq.json in your project to avoid retyping flags. Generate a starter with poriq init.
{
"port": 3000,
"subdomain": "myapp",
"tunnels": [{ "port": 3000 }, { "port": 8080 }],
"inspect": 4040,
"log": "requests.jsonl",
"auth": "user:pass",
"allow_ips": ["10.0.0.0/8"],
"rate_limit": 50,
"rate_limits": { "/api/webhook": 10 },
"mocks": { "/health": { "status": 200, "body": "ok" } },
"path_rewrites": { "/api/v1": "/v1" },
"header_add": ["X-From: poriq"],
"notify_on_error": true,
"timeout": "30s"
}Running poriq http with no arguments picks up this file.
Security & Access Control
Gate a tunnel without touching your app:
poriq http 3000 --auth admin:s3cret # HTTP Basic auth poriq http 3000 --allow-ip 1.2.3.4,10.0.0.0/8 # IP allowlist poriq http 3000 --rate-limit 50 # 50 req/s cap poriq http 3000 --rate-limit-path /api/webhook:10
These run at the edge of your tunnel, so blocked requests never reach your local server.
Traffic Shaping
Mock endpoints, rewrite paths, and rewrite headers on the fly:
# Canned response (path:status:body)
poriq http 3000 --mock /health:200:ok
# Rewrite a path prefix before forwarding
poriq http 3000 --rewrite /api/v1:/v1
# Add / strip headers
poriq http 3000 --header-add "X-Env: tunnel" \
--response-header-remove "X-Powered-By"Wake-on-LAN
Register Wake-on-LAN targets so you can boot machines on your LAN remotely through the tunnel (and the dashboard).
poriq http 3000 --wol "desktop=192.168.1.20" poriq http 3000 --wol "desktop=AA:BB:CC:DD:EE:FF"
IPv4 lookup uses your local ARP/neighbor cache at startup. poriq only sends magic packets for MACs explicitly registered or resolved on the tunnel client machine.
Plans & Limits
Free
$0- 2 HTTP tunnels
- Random subdomains
- Short sessions, manual reconnect
- 50 MB / day
- 5K requests / day
Pro
$5/mo- 5 HTTP + 2 TCP
- 3 custom subdomains
- Configurable TTL, default off
- 2 GB / day
- 100K requests / day
Team
$15/mo- 10 HTTP + 5 TCP
- 10 custom subdomains
- Configurable TTL, default off
- 10 GB / day
- 1M requests / day
Paid plans can still be limited by server policy. The normal paid default is TTL off, while trial, promo, and PoC deployments can use explicit TTLs such as 1h or 8h. TCP/UDP inherit the same duration policy when a plan sets one.
HTTP request bodies stream up to 100 MB. Self-hosting removes all limits. See pricing for details.
Self-Hosting
poriq is MIT-licensed and runs as a single binary. Host your own server with zero limits on tunnels, bandwidth, and sessions — your infra, your rules.
# build the server go build -o poriq-server ./cmd/poriq-server # run it (HTTP + tunnel API on one process) ./poriq-server --domain example.com --port 9090
Point clients at it with --server wss://example.com. Full guide on GitHub.
CLI Reference
poriq login authenticate with GitHub poriq logout remove saved credentials poriq status show auth status poriq http <port...> create HTTP tunnel(s) poriq tcp <port> create a TCP tunnel poriq udp <port> create a UDP tunnel poriq inspect <file> view a saved request log poriq init create a poriq.json config poriq version show version info
Run any command with --help to see its full flag set.
Ready to tunnel? Sign in with GitHub and run poriq http 3000.