· via Hacker News – Front Page (native)
Vincent Bernat's guide builds HTTP tunnels from OpenSSH and Nginx alone
A blog post that reached Hacker News explains how to expose a localhost service through your own server using OpenSSH remote forwards, wildcard DNS and Nginx, with signed expiring URLs instead of a commercial tunnel service.

What the guide covers
A blog post by Vincent Bernat, which reached the front page of Hacker News, walks through building a tunnel service comparable to ngrok using only two components most servers already run: OpenSSH and Nginx. The motivating scenario is familiar — a work-in-progress preview running on localhost:8080 that a colleague elsewhere needs to see.
Bernat sorts the existing options into three groups: hosted commercial services such as ngrok or Cloudflare Quick Tunnels; self-hostable tools that require their own client, like frp or localtunnel; and projects such as sish that accept a standard SSH client but need a custom SSH server on the far end. His setup avoids every one of those dependencies.
How the tunnel works
The core mechanism is SSH's remote port forwarding. Running ssh -N -R 0:localhost:8080 server asks the remote host to open a listening port and relay connections back to the local service. Passing 0 as the port lets the server pick a free one, which avoids collisions but means the client does not know the number in advance — a problem the guide solves later.
On the server, Nginx maps a per-tunnel subdomain to that port. A regular-expression server_name captures the port digits from hostnames like p41535.ssh.example.com and proxies requests to the matching loopback port. The supporting cast is a wildcard DNS record pointing at the server and a wildcard TLS certificate from Let's Encrypt. Bernat issues certificates through DNS-01 challenges handled by a separate Route 53 zone, with NixOS automating renewal, though any ACME setup would work.
The result is already functional, but as the post notes, the port number is the only thing standing between the public internet and the tunneled content, and ports can simply be enumerated. Other tools counter this by embedding an unguessable string in the hostname.
Signed, expiring URLs
To close that gap, the guide turns to Nginx's built-in ngx_http_secure_link module. It computes a hash over the expiry timestamp, the port number and a shared secret, then rejects any request whose hash does not match. Because the hash is base64-encoded and domain names are case-insensitive, it cannot live in the subdomain; instead it is placed in the URL's userinfo field, the segment before the @ sign. Browsers and clients such as curl transmit this as HTTP Basic authentication credentials, Nginx exposes it through the $remote_user variable, and a map directive splits out the hash and its timestamp.
Error handling is explicit: a missing or wrong hash returns 401 along with a WWW-Authenticate header prompting for credentials, while a valid but expired link returns 410. The configuration also strips the Authorization header before proxying and sets the upgrade headers that WebSocket connections need.
Generating a link is a one-liner: pipe the timestamp, port and secret through openssl md5, base64-encode the digest, and translate the characters into a URL-safe alphabet.
A wrapper to make it usable
The remaining inconvenience is discovering which port OpenSSH chose, since it is not exposed in any environment variable. Bernat's helper script, installed on the server as http-over-ssh, walks its own process ancestry to find the governing sshd-session processes, then uses ss with non-interactive sudo to list the ports those processes are listening on. For each port it computes a token valid for 24 hours, prints the full shareable URL, and sleeps so the SSH session stays open.
A short ~/.ssh/config entry using RemoteCommand ties it together, so a single invocation — ssh with remote forwarding to the http-over-ssh alias — allocates the port and prints a URL ready to hand to a reviewer. A NixOS module version is offered alongside the plain script.
Why it matters
Commercial tunnel services are convenient, but they route potentially private traffic through a third party and add an external dependency whose terms and tiers can shift. The post demonstrates that equivalent capability already sits inside infrastructure many operators keep anyway: a VPS running OpenSSH and Nginx, plus a wildcard DNS record and certificate. The underlying pattern — ephemeral remote forwards, regex-based virtual hosts and expiring signed links — is reusable well beyond blog previews, from webhook testing to temporary demos. The trade-offs are real: you need a server, a domain you control, and some comfort with Nginx configuration. For anyone who already runs both pieces of software, it is a template for dropping one more SaaS subscription.
- #ssh
- #nginx
- #self-hosting
- #tunneling
- #devops