- Published on
Self-hosted HTTP tunnels with SSH and Nginx
- Authors

- Name
- DeepHorus
Vincent Bernat demonstrated a self-hosted HTTP tunnel for localhost:8080 using only OpenSSH and nginx. His approach replaces commercial services such as ngrok and Cloudflare Quick Tunnels with familiar server tools. It avoids the dedicated clients or SSH servers required by frp, localtunnel, and sish.
He connects to web02.luffy.cx with a remote SSH forward, and the example allocates port 41535 for traffic to localhost:8080. Using zero as the requested remote port makes the server select a free port. Nginx proxies p41535.ssh.luffy.cx on port 443 to 127.0.0.1:41535.
Wildcard DNS for *.ssh.luffy.cx points to web02.luffy.cx, while a CAA record permits Let’s Encrypt wildcard issuance. An ACME DNS-01 challenge uses a zone hosted on Route 53. Bernat’s NixOS setup retrieves the certificate automatically.
The basic design treats the allocated port as its only secret, leaving URLs vulnerable to enumeration. Bernat therefore adds nginx’s ngx_http_secure_link_module. The URL places a 22-character hash and expiration timestamp in the HTTP-basic-auth username, ahead of the encoded port and path.
Nginx exposes the username as $remote_user, and a map directive extracts the two values. The module checks a hash derived from the expiration, port, and a shared secret. Because the token uses standard HTTP authentication, clients such as curl can request the tunnel.
The module returns an empty status for a bad hash, “0” for an expired link, and “1” for a valid one. Nginx responds with 401 and a WWW-Authenticate header when credentials fail, or 410 when expiration is reached. It also strips the Authorization header before forwarding and adds WebSocket support.
The system matters because it combines conventional server software with expiring URLs for temporary previews. Operators must manage DNS, TLS, proxy rules, shared secrets, and server security. The model can be extended by changing the hostname pattern, hash inputs, and nginx behavior.