折腾背景:序章里我把 Tailscale / 自建 Headscale 的原理讲过了,假设你已经有一套 Headscale 跑起来。但 Headscale 原生只有命令行——加节点、批准设备、建用户、配子网路由,全得 SSH 上去敲
headscale命令。设备一多就烦。headscale-ui 是社区做的一个纯静态 Web 前端,直接调 Headscale 的 API,把这些操作搬进浏览器。这篇记一下它的安装和两个反代/登录的坑。
它是怎么工作的
headscale-ui 本身没有后端,就是一堆静态 HTML/JS。它在你浏览器里,拿着你填的 API Key 直接去敲 Headscale 的 HTTP API。所以部署的核心其实是两件事:
- 把这堆静态文件托管起来(Docker 或塞进 nginx 目录)。
- 用反向代理把"静态 UI"和"Headscale API"放到同一个域名下,避免浏览器跨域(CORS)。
全流程总览
标橙的第③步(反代同域)是最容易卡住的——跨域和 HTTPS 没配对,UI 会一直转圈连不上 API。
前置
- 一台已经在跑的 Headscale(本文假设它监听在本机
:8080,对外域名vpn.e7coding.com)。 - 一个能签证书的反代(nginx 或 Caddy),UI 强烈建议走 HTTPS。
第 1 步:把 headscale-ui 跑起来
两种方式选一种。
方式 A:Docker(推荐,省事)
docker run -d \
--name headscale-ui \
--restart unless-stopped \
-p 9443:443 \
ghcr.io/gurucomputing/headscale-ui:latest
容器里就是个装好静态文件的小 web server,监听 443(映射到宿主 9443)。后面反代把 /web 指过来即可。
方式 B:静态文件 + nginx(想完全自己掌控)
去 Releases 下最新的发布包,解压到 nginx 的站点目录:
mkdir -p /var/www/headscale-ui
unzip headscale-ui.zip -d /var/www/headscale-ui
然后让 nginx 把 /web 映射到这个目录(见下一步的配置)。
第 2 步:生成 API Key
headscale-ui 靠 API Key 鉴权,在 Headscale 服务器上生成一个:
# 创建一个有效期较长的 key(按需调整)
headscale apikeys create --expiration 999d
# 输出形如:
# xxxxxxxx.yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
把这串 key 完整复制下来,等会儿要填进 UI。它只在创建时完整显示一次。
# 顺手可以列出 / 过期废弃旧 key
headscale apikeys list
第 3 步:反向代理,同域暴露 UI 和 API(关键)
核心是把 /web(UI)和 Headscale 的 API 路径放在同一个域名下。下面是 nginx 的示意配置(用方式 A 的容器为例):
server {
listen 443 ssl;
server_name vpn.e7coding.com;
ssl_certificate /etc/nginx/certs/vpn.e7coding.com.crt;
ssl_certificate_key /etc/nginx/certs/vpn.e7coding.com.key;
# ① Web 界面:转发到 headscale-ui 容器
location /web {
proxy_pass https://127.0.0.1:9443;
proxy_set_header Host $host;
}
# ② Headscale API / 控制面:转发到 headscale
location / {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
用方式 B(静态文件)的话,把
location /web换成alias /var/www/headscale-ui;直接由 nginx 出静态文件即可,其余不变。重载:nginx -t && systemctl reload nginx。
第 4 步:打开 UI,填入配置
浏览器访问:
https://vpn.e7coding.com/web/
进设置页,填两样东西:
- Headscale URL / API URL:
https://vpn.e7coding.com(你的对外域名,不要带 /web) - API Key:第 2 步生成的那串
保存后,正常就能看到节点(devices)列表了。我自己最终落在 https://vpn.e7coding.com/web/devices.html 这个页面管设备。
第 5 步:验证
能看到节点列表、能批准/删除设备、能建用户,就算通了。常用的几件事现在都能点鼠标搞定:
- 批准新设备入网(配合 DERP 那篇里
tailscale up后生成的 register 链接) - 建 / 删用户
- 给节点配子网路由、打 tag
- 管理、废弃 API Key
踩坑小结
- 跨域(CORS):UI 和 API 必须同域。如果 UI 在
a.com、API 在b.com,浏览器会拦截请求,UI 一直连不上。用反代把它们收到一个域名下是最稳的做法。 - HTTPS 几乎是必须:很多浏览器对混合内容(HTTPS 页面请求 HTTP 接口)会直接拦。UI 走 https,API 也走 https,省一堆事。
- API Key 只显示一次:创建时没存下来,就只能废弃重建。
- URL 别填成 /web:设置里填的是 Headscale 的根域名,不是 UI 自己的
/web路径,填错就一直 401 / 连接失败。 - API Key 会过期:
--expiration到期后 UI 突然全部失效、报鉴权错,记得提前headscale apikeys create续一个再去 UI 里换上。
有了 Web 界面,下一篇就能在 headscale-ui 里点几下,把自建 DERP 节点批准入网了 👉 自建 DERP 中继。
许可协议:CC BY-NC 4.0
更新于 2 个月前
觉得文章有帮助?点个赞吧!
0 条评论


