用 OpenSSH 与 Nginx 自建 HTTP 隧道:一条命令替代 ngrok
一、事件概述
Hacker News 首页在 2026-10-04 出现了一篇题为《Self-hosted HTTP tunnels with SSH and Nginx》的技术文章(53 分、11 条评论),作者在文中给出了一套只依赖 OpenSSH 和 nginx 的 HTTP 隧道方案。
问题的起点非常日常:朋友想帮你校对一篇还在草稿状态的博客文章,而预览服务只跑在本机的 localhost:8080 上。作者列举了几类现成方案:
- 商业服务:ngrok、Cloudflare Quick Tunnels;
- 可自托管但需要专用客户端:frp、localtunnel;
- 只需普通 SSH 客户端、但依赖特定 SSH 服务端实现:sish。
作者的思路是:既然服务器上本来就在跑 OpenSSH 和 nginx,为什么不直接用它们拼出一个自托管隧道?最终效果是一条命令:
1 | $ ssh -R 0:localhost:8080 http-over-ssh |
命令执行后直接打印出一个带签名、带过期时间的可分享 URL。作者使用的域名是 luffy.cx,服务器是 web02.luffy.cx;原文未说明该域名或服务器的更多背景。
二、关键技术点
1. SSH 远程端口转发:-R 0
1 | $ ssh -N -R 0:localhost:8080 web02.luffy.cx |
把远程端口指定为 0 时,SSH 服务端会自行分配一个空闲端口,并把它回显出来。这一步就把本地 8080 暴露到了服务器的 127.0.0.1:41535 上。注意 -N 表示不执行远程命令,只做转发。
2. nginx:正则 server_name + 动态 proxy_pass
核心配置只有几行,靠 server_name 的正则捕获端口号,再拼进 proxy_pass:
1 | server { |
也就是说,访问 https://p41535.ssh.luffy.cx 就会被代理到 http://127.0.0.1:41535,从而命中 SSH 建立的转发。
3. DNS 与通配符证书
需要为 *.ssh.luffy.cx 配置 DNS,并通过 Let’s Encrypt 申请通配符证书。文中的做法是:
1 | *.ssh.luffy.cx. CNAME web02.luffy.cx. |
acme.luffy.cx 是托管在 Route 53 上的一个 zone,作者用它来处理 ACME DNS-01 挑战——既用于通配符证书,也用于多台 Web 服务器承载的域名。作者使用 NixOS,证书是自动获取的。
4. 访问控制:端口不是可靠的秘密
作者明确指出:端口本身是唯一保护内容机密性的”秘密”,而这个秘密的熵很低。内核从本地端口范围里分配端口,作者实测:
1 | $ sysctl -n net.ipv4.ip_local_port_range | awk '{print $1"—"$2" ≈ "log($2-$1+1)/log(2)" bits"}' |
而且内核在选择随机空闲端口时偏向奇数端口,这意味着还要再损失一位熵。
