tailscale-proxy

Configuration

tsp configure [flags] writes ~/.tailscale-proxy/config.json (created on first use). Flags always override the config for a single run; the file is the source of defaults, and a bare tsp runs start with it.

tsp configure --ports 3000-9000 --runtimes node,bun,python --private tsp # uses the saved config

Settings resolve left-to-right — each layer overrides the one before it:

File format

{ "ports": "3000-5000", "all": false, "runtimes": "", "private": false, "bind": "127.0.0.1", "port": 8443, "interval": 20, "httpsPort": 443, "logRequests": true, "deregisterCycles": 5, "forwardHost": false, "acceptDns": "" }
KeyMeaning
portsPort range ("3000-5000") or a single port ("4000") to scan
allInclude every listener, not just known web runtimes
runtimesComma-separated allow-list, e.g. "node,bun,python" (empty = all known)
privateExpose via Tailscale Serve (tailnet-only) instead of Funnel
bindProxy listen address (0.0.0.0 to reach it from containers/LAN)
portLocal proxy HTTP port
intervalRe-scan period, seconds
httpsPortPublic/tailnet HTTPS port (Funnel: 443 / 8443 / 10000)
logRequestsLog each proxied request
deregisterCyclesConsecutive missing scans before a service is removed
forwardHostForward the public host to apps (X-Forwarded-Host/-Proto)
acceptDns"" leaves Tailscale DNS alone; "true"/"false" runs tailscale set --accept-dns on start

Host header behavior

By default tsp presents a local request to your app: Host: localhost:<port>, the /<slug>/ prefix stripped, and X-Forwarded-For preserved — so the app behaves exactly like it does on localhost (CORS, cookies, host-allowlists all match). Set forwardHost: true (or --forward-host) to surface the public host via X-Forwarded-Host + X-Forwarded-Proto: https for apps that need the public URL (OAuth callbacks, canonical links).