不装 Nginx,用 Tailscale 给几十个本地服务搭个 HTTPS 私有传送门

先问一个问题:你的家庭主机、软路由或本地开发机上,现在常驻了多少个 Web 服务?

网盘挂载、本地大模型 API、聚合搜索引擎、离线下载器、知识库检索、语音转文字接口、监控看板……不知不觉间,机器上可能就挤了十几个甚至几十个端口。

随之而来的痛点非常真实:

  1. 端口记不住:这个是 5244,那个是 8088,另一个是 11434。每次在手机或平板上想用,先得翻半天备忘录查端口号;
  2. 没有正经证书:直接通过 IP+端口访问不仅格式难看,浏览器和 iOS 还会不停弹 SSL 不安全警告;
  3. 传统反代太重:起一个 Nginx 或 Caddy,改配置、管进程、搞证书自动续期,对只想在内网安稳访问的人来说负担不小。

其实,如果你已经在用 Tailscale,它自带的 tailscale serve 就能以极轻量的方式彻底解决这个问题:无需额外装反代,用一个自带合法 HTTPS 证书的节点域名,按子路径把所有本地端口整整齐齐地收拢起来。

🎯 方案长什么样?

配置完成后,你在 Tailnet 私有网内的任何设备(手机、平板、笔记本)上,访问形式都会变成统一的规范路径:

一台主机通过私有入口按路径分发到六个本地服务

全程端到端加密、自带由 Let’s Encrypt 签发的官方信任证书,并且纯私有网络运行,彻底阻断公网扫描。

💡 为什么选 Tailscale Serve 而不是 Nginx?

在单机聚合场景下,Tailscale Serve 有三个不可替代的优势:

  • 零额外守护进程:它直接由系统常驻的 tailscaled 提供调度,无需再额外跑一个 Web 服务器去吃内存;
  • 全自动合法证书:Tailscale 官方深度集成了自动化证书,不用自己配 ACME、不用配 DNS API 密钥,开箱即是原生小绿锁;
  • 天然网络层隔离:默认监听在 WireGuard 虚拟网卡上,物理局域网和外网防火墙完全不需要开任何端口。

⚠️ 实践踩坑与核心细节

在实际落地时,有三个核心机制必须提前了解:

. 路径剥离机制(Path Stripping)

这是使用子路径反代最容易让人疑惑的一点:

Tailscale Serve 的 --set-path /path 代理,默认会将前缀路径剥离后再发送给后端目标。

  • 举个例子: 如果配置了 --set-path /search http://127.0.0.1:8088,当你在浏览器请求 GET /search/api/v1/query 时,本地 8088 端口收到的实际请求路径是 GET /api/v1/query。
  • 适配建议: 所有纯 API 接口(如 Ollama、语音识别、搜索后端)和支持相对路径的应用都能无缝兼容;若是静态资源强制引用根路径(如写死 /assets/app.js)的前端单页应用,需在应用中指定 Base URL,或通过导航聚合页作为跳板直接访问。

. macOS 上的沙盒机制限制

如果尝试直接把本地某个静态网页目录挂载到根路径 / 做导航:

1
tailscale serve --bg --set-path / /path/to/static_dir

macOS 上的 Tailscale.app 会由于系统 App Sandbox 权限直接报错拦截。

优雅解法:不挂载静态目录,而是用几十行轻量 Python 脚本在本地监听一个端口(比如 127.0.0.1:8899),专门渲染一个自适应的导航门户,然后再用反代挂载到根路径:

1
tailscale serve --bg --set-path / http://127.0.0.1:8899

不仅绕过了沙盒,还能让导航页具备按关键词实时模糊过滤卡片的动态交互能力。

. 安全红线:严格区分 Serve 与 Funnel

Tailscale CLI 里有两个命令:

  • tailscale serve:流量仅在你的 Tailnet 私有虚拟网络内部流转,外部公网完全不可见;
  • tailscale funnel:通过 Tailscale 官方的公网中继节点,向全球互联网公开该域名,全球任何设备无需密码均可直连。

对于跑了私人数据、本地管理面板和下载工具的主机,严禁开启 Funnel。

如果不小心开过 Funnel,可以使用以下命令彻底重置公网转发:

1
tailscale funnel reset

随后通过 tailscale serve status 检查,必须确保状态标记为 (tailnet only),绝不能出现 (Funnel on)。

🛠️ 可直接复用的自动化维护脚本

为了保证在机器重启或规则调整后能够一秒恢复,可以将所有路由写进一个自动化维护脚本 reload_serve.sh:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#!/usr/bin/env bash
set -e

DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"

if ! lsof -iTCP:8899 -sTCP:LISTEN -P -n >/dev/null 2>&1; then
nohup python3 "$DIR/portal_server.py" > "$DIR/portal.log" 2>&1 &
sleep 1
fi

python3 -c "
import subprocess

routes = [
('/', 'http://127.0.0.1:8899'),
('/storage', 'http://127.0.0.1:5244'),
('/search', 'http://127.0.0.1:8088'),
('/llm', 'http://127.0.0.1:11434'),
('/download', 'http://127.0.0.1:18888'),
('/dashboard','http://127.0.0.1:8080'),
]

for path, target in routes:
cmd = ['tailscale', 'serve', '--bg', f'--set-path={path}', target]
subprocess.run(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
print('所有路径规则已成功注册。')
"

echo "Tailscale Serve 正在运行中 (TAILNET ONLY 私有模式):"
tailscale serve status

日常只需运行此脚本,3 秒内所有服务路径即全部自动就绪。

📋 总结

通过 Tailscale Serve 治理本地零散服务,核心收益在于:

  1. 省心:一个域名收归所有入口,彻底告别“翻备忘录找端口”;
  2. 轻量:不依赖第三方反代,全自动 Let’s Encrypt 证书;
  3. 克制:守住 tailnet only 安全底线,数据和控制权始终留在私有网络之内。