HTTP Tunnel
Learn how to start an HTTP tunnel with the Portr CLI to expose a local web server or API to the public internet in a single command.
Use the following command to tunnel an HTTP connection.
Or start it with a custom subdomain:
This also starts the portr inspector on http://localhost:7777 by default, where you can inspect and replay HTTP requests. Change dashboard_port in the client config to move it to another local port, or set disable_dashboard: true to skip starting it entirely.
Rewriting the Host header
By default, Portr forwards the public host (amal-test.portr.dev) to your local server. Frameworks with host allowlists — Rails host authorization, Django ALLOWED_HOSTS, Vite server.allowedHosts — reject those requests.
rewrite replaces the Host header with the local address (localhost:9000). Any other value is sent literally, which is useful for vhost-based local setups:
Or per tunnel in the config file:
rewrite changes the hostname your app believes it is served from, so absolute URLs and redirects it generates will point at the local address. If that matters, leave host_header unset and add the public host to your framework's allowlist instead.
host_header is only valid on type: http tunnels. The inspector always records the original public host, not the rewritten one.
Password-protecting a tunnel
A tunnel URL is public as soon as it starts. basic_auth puts HTTP basic auth in front of it, so visitors get their browser's credential prompt and only authenticated requests reach your local server.
Flags can appear anywhere on the command line — before or after the port, in any order.
The value is user:password, split on the first colon — your password may contain colons, the username may not. Because a credential on the command line is visible in the process table and lands in your shell history, you can pass it through the environment instead:
Or per tunnel in the config file:
basic_auth works on http, stub and static tunnels. It is rejected on tcp tunnels, which carry no HTTP requests to challenge, and on team templates, which are shared verbatim in plain text and cannot keep a credential secret.
Rejected requests are recorded in the inspector as 401, with the attempted credential redacted, so you can see what is hitting the tunnel.
Basic auth is base64-encoded, not encrypted. Over the default https:// tunnel URL it is protected by TLS, but if you run with use_localhost: true the tunnel URL is plain http:// and the credentials travel in the clear.
A few behaviours worth knowing:
- Your app still sees the header. Portr validates
Authorizationand forwards it unchanged, so a local app checking the same credential keeps working. An app expecting a differentAuthorizationvalue cannot work behindbasic_auth— a browser only sends one. - WebSocket handshakes are gated too. Browsers replay cached credentials on the upgrade, but non-browser clients must send the header themselves (
wscat -H "Authorization: Basic ..."). A failed upgrade shows up as401with no credential prompt. - CORS preflights are gated. Browsers do not send
Authorizationon anOPTIONSpreflight, so cross-origin JavaScript cannot reach abasic_authtunnel. - There is no logout. Browsers cache basic credentials per origin for the session, so changing
basic_authdoes not evict a credential a browser already has. - Replaying a request returns
401. The inspector redactsAuthorizationwhen it stores a request, so a replay re-sends the redacted placeholder. Set the header explicitly in the replay dialog, or withportr replay --header "Authorization: Basic ...".
How it works
When you run the HTTP tunnel command:
- Portr connects to your configured server
- Creates a secure tunnel from your local port to a public URL
- Starts the request inspector interface locally
- Routes all incoming requests through the tunnel to your local service
Inspector Features
The Portr inspector provides powerful debugging capabilities:
- Request inspection: View all incoming HTTP requests in real-time
- Request replay: Replay a stored request as-is or edit it before resending
- Headers analysis: Examine request and response headers
- Payload viewing: Inspect request and response bodies
- WebSocket inspection: Monitor upgraded WebSocket sessions and captured frames
Common Use Cases
- Local web development: Share your local development server with others
- Webhook testing: Receive webhooks from external services during development
- API debugging: Test API integrations with external services
- Demo presentations: Show your work to clients or team members