---
name: novnc-web-desktop
description: "在浏览器中访问远程桌面 - 部署 TigerVnc + websockify + noVNC，通过 cloudflared 隧道公网暴露，浏览器直接打开域名即可使用 VNC 桌面，无需安装客户端。关键词：noVNC、VNC、websockify、远程桌面、浏览器VNC、cloudflared、cfqt、web桌面、远程控制"
version: "1.0.0"
author: "WorkBuddy User"
created: "2026-08-24"
---

# noVNC Web 远程桌面

> **Goal**: 在任意 Ubuntu 设备上部署 noVNC，让用户通过浏览器直接访问 VNC 桌面，无需安装客户端，经 cloudflared 隧道公网可达。

## When to Use

- 用户说"我想用浏览器连 VNC / 远程桌面"
- 用户说"怎么从公网访问我的桌面"
- 用户说"noVNC 怎么部署 / 安装"
- 用户说"我想要一个别人不用装客户端就能连的远程桌面"
- 用户说"把 VNC 暴露到公网 / 域名"
- 用户说"websockify 怎么配"

## When NOT to Use

- 用户要连的是 Windows 远程桌面（RDP）— 用 mstsc / xrdp，不是本 skill
- 用户要的是 SSH 终端 — 不是 VNC 桌面
- 用户的设备没有图形桌面环境（纯 server 无 GUI）— 需先装桌面环境
- 用户的设备不是 Ubuntu/Debian 系 — 脚本用 apt，不适用 RHEL/Alpine

## 工作原理（排障前必读）

```
用户浏览器 ──HTTPS──> cloudflared 隧道 ──> websockify :6080 ──> Xtigervnc :5901 ──> XFCE4 桌面
                         (cfqt 域名)      (WebSocket→TCP 转换)    (VNC RFB 协议)     (X11 显示器 :1)
```

### 关键认知

1. **VNC 是裸 TCP（RFB）协议，不是 HTTP**。浏览器不能直连 VNC，必须经 websockify 做 WebSocket→TCP 转换。
2. **VNC 端口必须绑 `127.0.0.1`**（`-localhost=1`），公网只通过 websockify :6080 暴露。这是安全最佳实践。
3. **cfqt（cloudflared quick tunnel）的 `--url http://` 只代理 HTTP 流量**。websockify 把 VNC 包成了 HTTP/WebSocket，所以能走 cfqt 隧道。
4. **cfqt 域名无鉴权公开** — 任何人拿到域名都能访问。VNC 密码是唯一防线。
5. **`index.html → vnc.html` 软链是必须的** — 否则 websockify 内置 web server 回退到列目录，用户看到的是文件列表而非 noVNC 界面。
6. **websockify 只能启动一个实例** — 多实例会端口冲突或行为异常。

### 为什么不能用原生 VNC 客户端直连 cfqt 域名？

- cfqt `--url http://` 只代理 HTTP，原生 VNC Viewer 发裸 TCP，协议不匹配。
- cfqt `--url tcp://` 虽支持 TCP，但客户端也需装 cloudflared 做 `cloudflared access tcp` 本地转发，普通 VNC Viewer 直填域名连不上。
- noVNC（浏览器）是最简单的方案：零客户端安装，浏览器打开即用。

## 部署流程（AI 按序执行）

### Step 0: 环境自检（缺啥装啥）

```bash
# 确认系统
cat /etc/os-release | grep -E '^ID=|^VERSION_ID='
uname -m

# 确认 cloudflared 是否已安装
cloudflared --version 2>/dev/null && echo "cloudflared ✅" || echo "cloudflared ❌ 需安装"
```

如果 cloudflared 未安装：
```bash
curl -L https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64 -o /usr/local/bin/cloudflared
chmod +x /usr/local/bin/cloudflared
```

如需更多前置环境（XFCE 桌面等），参考 `docs/prereq-vnc-desktop-setup.md`。

### Step 1: 部署脚本文件

```bash
# 确保脚本已就位（从 skill 包解压后执行）
cp scripts/setup-novnc.sh scripts/start-novnc.sh scripts/expose-novnc.sh /workspace/
chmod +x /workspace/setup-novnc.sh /workspace/start-novnc.sh /workspace/expose-novnc.sh
```

### Step 2: 一键安装依赖 + 配置

```bash
cd /workspace && ./setup-novnc.sh
```

此脚本会：
- apt 安装 tigervnc-standalone-server、novnc、python3-websockify、xfce4、xterm
- 创建 VNC 配置目录 `/root/.vnc/`
- 写入 xstartup（XFCE4 桌面 + 中文 locale）
- 建立 `index.html → vnc.html` 软链（**关键步骤，不做则根路径显示目录列表**）

### Step 3: 设置 VNC 密码【人工步骤，必须停下】

> ⚠️ **此步骤需要用户交互，AI 必须停下让用户操作。**

告诉用户执行：

```bash
vncpasswd
```

输入两次密码（6-8 位）。密码文件会写入 `/root/.vnc/passwd`。

确认用户完成后继续。

### Step 4: 启动 VNC + websockify

```bash
cd /workspace && ./start-novnc.sh
```

此脚本会：
- 启动 Xtigervnc :1（绑定 127.0.0.1:5901，-localhost=1，1280x720）
- 启动单实例 websockify（:6080 → localhost:5901，--web /usr/share/novnc）
- 检查 index.html 软链
- 防重复启动（已在跑则跳过）

验证本地：
```bash
# 6080 应返回 noVNC HTML 页面（含 noVNC_loading 标记）
curl -s http://localhost:6080/ | head -3

# 5901 应仅在 127.0.0.1 监听
ss -ltnp | grep 5901
```

### Step 5: 启动 cloudflared 隧道

```bash
cd /workspace && ./expose-novnc.sh
```

此脚本会：
- 启动 `cloudflared tunnel --url http://localhost:6080`
- 等待域名分配（约 5-10 秒）
- 输出公网域名到终端

### Step 6: 端到端自测（AI 自行验证）

```bash
# 从脚本输出获取域名后：
DOMAIN=$(cat /tmp/novnc_cfqt_domain.txt)

# 1. 公网可达
curl -s -o /dev/null -w "HTTP %{http_code}" "$DOMAIN/"

# 2. 返回 noVNC 页面（不是目录列表）
curl -s "$DOMAIN/" | grep -c "noVNC"

# 判定：
# HTTP 200 + noVNC 标记 >= 1 → SUCCESS
# 否则 → 参见排障表
```

## 排障表

| 症状 | 根因 | 处置 |
|------|------|------|
| 打开域名显示目录列表（app/ core/ vnc.html） | 缺 `index.html → vnc.html` 软链 | `cd /usr/share/novnc && ln -sf vnc.html index.html` |
| noVNC 页面打开但连不上，提示连接失败 | VNC 未启动或端口不对 | 确认 5901 在监听：`ss -ltnp \| grep 5901`；确认 websockify 指向 5901 |
| noVNC 连上但黑屏 | xstartup 未配置桌面 / 桌面环境未安装 | 确认 xfce4 已装；检查 `/root/.vnc/xstartup` 是否 `exec startxfce4` |
| websockify 启动报端口占用 | 多实例冲突 | `pkill -f "websockify.*6080"` 然后重启 |
| cfqt 域名打不开 / 超时 | 隧道进程已死 | `pkill -f "cloudflared.*6080"` 然后重跑 `expose-novnc.sh` |
| VNC 提示密码错误 | 密码文件不匹配 | 重跑 `vncpasswd`，确认写入 `/root/.vnc/passwd` |
| 5901 端口从外部可达 | 未加 -localhost=1 | 重启 VNC 时确保带 `-localhost=1` 参数 |
| cfqt 域名变了 / 失效 | 进程重启 / 超时回收 | cfqt 域名不固定，重跑 `expose-novnc.sh` 获取新域名 |

## 交给用户的使用说明（部署成功后转述）

部署完成！你的 noVNC Web 桌面已上线。

**访问方式**：浏览器打开域名 → 输入 VNC 密码 → 进入桌面。

**注意事项**：
- 域名是 cloudflare 临时隧道分配的，**重启隧道域名会变**，重跑 `expose-novnc.sh` 获取新域名。
- VNC 密码是唯一安全防线，请设置足够强度的密码。
- 如需固定域名，需注册 Cloudflare 账号配置命名隧道。
- 浏览器关闭后 VNC 会话仍在，下次连接恢复之前的桌面状态。
