---
title: "用手机接着用电脑上的 Pi"
description: "不必先学内网穿透。按手机是否要同时开其他 VPN 应用，选 Tailscale 或 Cloudflare，只走一条路线；附 Windows／WSL 配置、验收、每次开机后的命令和远程桌面的区别。"
url: https://eigentime.org/zh/blog/pi-web-on-phone/
language: zh-CN
published: 2026-10-22
updated: 2026-10-23
tags: ["ai-agent", "cloudflare"]
license: "CC BY-NC-SA 4.0 <https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh-hans>"
---

# 用手机接着用电脑上的 Pi

离开电脑后，用手机看看回答，补一句要求，让电脑继续做事。先实现这个目标，不必先买服务器，也不用先弄懂路由器端口映射。

本文用 Pi Web 举例，方法也适用于其他已经在电脑上运行的网页工具。示例环境是 Windows + WSL2（Windows 中的 Linux 环境）、Android 手机；示例环境里，路线 A 已经走通：手机关掉 Wi-Fi、用蜂窝网络打开电脑上的 Pi Web，登录后能看到会话、收到回复。路线 B 还没有实测，只按官方文档整理，文中标明了需要自己核对的地方。

## 先选一条路，另一条不用读

只问一个问题：使用 Pi Web 时，手机是否必须同时开着另一个 VPN 类应用？

- 不需要，或可以暂时关掉它：走 [A：Tailscale 私有连接](#route-a)。网页内容只在自己的电脑上解开，不用买域名。
- 必须同时开：走 [B：Cloudflare 固定网址](#route-b)。手机只用浏览器，代价是网页内容会先在 Cloudflare 解开，再转给你的电脑。

如果“必须同时开着另一个 VPN 应用”和“不让第三方解开内容”都是硬要求，这两条入门路线不能同时满足。先不要配置 B；需要另评估只转发密文、本机负责 HTTPS 的中继方案。

示例环境里，手机另装有一个 VPN 类应用，但不常开。Android 同一用户资料同时只能有一个活动 VPN（[Android 开发者文档](https://developer.android.com/develop/connectivity/vpn)），Tailscale 也说明了 [它不能与其他 VPN 同时运行](https://tailscale.com/docs/reference/faq/other-vpns)；所以先关掉它试 A，不必卸载，也不必换成别的软件。

自己的域名只在 B 中使用。A 用 Tailscale 分配的 `*.ts.net` 地址，不要把自己的域名硬填进去。

<details>
<summary>两条路线到底怎么连？谁能看到什么？</summary>

把 HTTPS 想成一个封好的信封：路上的人只看得到信封，看不到信。两条路线的区别，在于信封在哪里拆开。

A：手机和电脑之间先搭一条只属于自己设备的加密通道，开启它的钥匙只存在两台设备上。直连不上时，数据会借道 Tailscale 的中继服务器，但中继没有钥匙，只能原样转交（[DERP 中继说明](https://tailscale.com/docs/reference/derp-servers)）。Tailscale 的登录服务器只帮设备互相“介绍”，交换的是公钥和设备信息（[Tailscale 的工作原理](https://tailscale.com/blog/how-tailscale-works)）。信封在你电脑上的 Serve 这里拆开，再交给本机的 Pi Web。

B：电脑上的 cloudflared 主动连到 Cloudflare，路由器不用开任何端口（[Cloudflare Tunnel 说明](https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/)）。手机打开固定网址时，信封在 Cloudflare 拆开，Access 先核对是不是你本人，再装进另一个信封经隧道送到电脑。访客到 Cloudflare、Cloudflare 到你的电脑，是两段各自加密的连接（[Cloudflare 加密模式](https://developers.cloudflare.com/ssl/origin-configuration/ssl-modes/)）。

| | A：Tailscale | B：Cloudflare |
| --- | --- | --- |
| 谁能打开 | 只有登录了你账号的设备 | 谁都能到门口，只有通过 Access 的本人能进 |
| 在哪解开 | 你的电脑 | 先在 Cloudflare，再到你的电脑 |
| 服务商知道 | 账号、设备和谁连了谁；[记录里没有流量内容](https://tailscale.com/docs/features/logging) | 账号、登录记录，以及经过它的网页内容 |
| 进门后 | 还要输 Pi Web 密码 | 还要输 Pi Web 密码 |

Pi Web 密码是第二道锁：设备被别人拿到、或访问规则配错时，还有它挡着。它本身不加密连接，Pi Web 的说明也要求远程使用放在 HTTPS 或可信 VPN 后面（[Pi Web 中文说明](https://github.com/agegr/pi-web/blob/main/docs/README.zh-CN.md)），这两条路线都满足。

</details>

<details>
<summary>只想偶尔点一下电脑：向日葵、UU 远程也可以吗？</summary>

可以。电脑和手机安装相应客户端，绑定本人设备后，在手机里控制电脑上的 Pi Web。通常不用另外配置本文的网页入口，远程控制功能一般也不占用第二个系统 VPN；具体版本仍需检查，手机上其他 VPN 应用的分流规则也可能影响连接。

两种体验有区别：远程桌面传输电脑画面和操作，能处理桌面弹窗、重启应用；手机网页把聊天内容按手机宽度排版，更适合长时间阅读和输入。前者在高画质、画面持续变化时通常更耗带宽，还受电脑屏幕分辨率影响。

远程桌面授权通常覆盖整台电脑，需保护账号、开启可用的二次验证，不分享连接码；不用的文件传输、剪贴板同步和无人值守权限可以关闭。厂商页面上的“加密”“隐私屏”不足以证明中继无法解密，本文没有核实这两款产品的完整加密实现，也没有做延迟排名。

如果已经装有一个可信的远程桌面工具，可以把它留作恢复电脑服务的备用入口，不必再装多个。没有特殊唤醒条件时，电脑关机或断网，它也不能凭空连上。

</details>

## 出门前，只确认电脑能打开

在运行网页工具的电脑上打开它的本地地址。Pi Web 默认是：

```text
http://127.0.0.1:30141
```

看到页面才继续。本地还打不开，先修本地服务；隧道不会把坏掉的应用修好。手机里的 `127.0.0.1` 指手机自己，不能直接复制这个地址到手机使用。

首次安装 Pi Web，先按官方说明检查 Node.js 版本，再全局安装发布包。日常远程使用不要用开发服务器替代正式运行方式。

<details>
<summary>首次安装／WSL 用户：展开这一步</summary>

本文实测用的 Pi Web 0.12.2 要求 Node.js 22.19.0 或更高版本：

```bash
node --version
npm install -g @agegr/pi-web@0.12.2
command -v pi-web
pi-web version
pi-web --hostname 127.0.0.1 --port 30141 --no-open
```

版本只写在安装命令里，用来复现本文；之后启动的都是已经安装的 `pi-web`，不会每次自动升级。`command -v pi-web` 应指向 npm 的全局安装目录，而不是 `.npm/_npx/` 临时缓存。npm 提示有安装脚本尚未批准时，不要批量批准。已有 Pi Web 正在占用 30141 时，不再启动第二个实例。选择与原有 Pi 相同的系统用户和文件系统环境，避免打开一套空会话。

示例中的 Pi Web 跑在 WSL，Windows 也需要能访问它。用 Windows 浏览器打开上述地址；或者在 PowerShell 执行只读检查：

```powershell
(Invoke-WebRequest -UseBasicParsing -Uri 'http://127.0.0.1:30141/' -TimeoutSec 10).StatusCode
```

预期为 `200`。示例机器的 WSL2 使用镜像网络，这一步已通过；其他机器以自己的结果为准。不通时先查 WSL 服务与本机访问，不要直接关防火墙或把监听地址改成 `0.0.0.0`。

macOS／Linux 原生运行时，后面的连接工具放在应用所在的同一台机器即可。

</details>

<a id="route-a"></a>

## A：手机和电脑建立私有连接

这条路只做三件事：让设备互相看见 → 给本地网页一个 HTTPS 地址 → 手机打开。第一次不用处理开机自启。

![路线 A 示意：手机经 Tailscale 的加密连接到自己的电脑，Serve 在电脑上解开 HTTPS，再交给本机 Pi Web；不是操作截图。](/images/posts/pi-web-on-phone/tailscale-path.svg)

### A1 · 两台设备出现在同一个列表里

从 [Tailscale 官方下载页](https://tailscale.com/download)安装 Windows 和 Android 客户端，用同一账号登录。点击登录后，浏览器可能要等几秒到十几秒才弹出登录页；先等一会儿，不要连续点击。手机先关掉其他 VPN 类应用，再开启 Tailscale。账号开启双重验证；这次不启用 [Exit Node](https://tailscale.com/docs/features/exit-nodes)（让手机其他上网流量也从某台设备出去的功能）。

打开设备列表，确认手机与电脑都在线。刚连上时手机可能经中继（relay）连到电脑，能用就行，这一步不用处理。仅在自己的新网络里试验，不把电脑分享给其他人。已有团队网络的用户，先核对访问规则是否只允许预期的本人设备访问这台电脑的 HTTPS 端口。

到这里可以停一下。今天只做到设备在线，也是一项完整的小进展。

<!-- 拍摄点 A1：设备列表，两台本人设备在线；遮住邮箱、IP 与设备密钥。 -->

### A2 · 让电脑提供 HTTPS 地址

首次配置按下面三步做。Windows 上运行 Serve，Pi Web 继续留在 WSL。先给 Pi Web 设置密码并核验，再打开远程入口；以后开机按后文的启动脚本恢复。

<details>
<summary>A2.1 · Windows：取得设备完整域名</summary>

在 Tailscale 管理控制台的 DNS 页面开启 [MagicDNS](https://tailscale.com/docs/features/magicdns)（设备名称解析）与 [HTTPS Certificates](https://tailscale.com/docs/how-to/set-up-https-certificates)。示例环境里 MagicDNS 默认已开，HTTPS Certificates 要自己打开；在控制台里开，比等命令行临时申请更稳。开启后，设备的完整域名会写进任何人都能查的公开证书记录。别人能查到这个名字，但连不进来；先把带真名、公司名的设备名改掉。

在 Windows PowerShell 查看设备完整域名：

```powershell
$ts = tailscale status --json | ConvertFrom-Json
$ts.Self.DNSName.TrimEnd('.')
```

记下输出，不带末尾的点、不带 `https://`。如果命令找不到，安装后重新打开 PowerShell，或使用安装目录里的 `tailscale.exe`。

</details>

<details>
<summary>A2.2 · WSL：保存启动脚本，带密码重启并检查</summary>

在 Pi Web 所在的 WSL Bash 中，先保存 A2.1 的完整域名。已有这个文件时命令会停下，不覆盖：

```bash
(
  set -eu
  umask 077
  file="$HOME/.config/pi-web/allowed-hosts"
  test ! -e "$file" || {
    printf '文件已存在，未覆盖，请先核对。\n'
    exit 1
  }
  read -r -p '粘贴完整设备域名（不含 https://）：' host
  [[ "$host" == *.ts.net && "$host" != *[[:space:]/:]* ]] || {
    printf '域名格式不对，未保存。\n'
    exit 1
  }
  mkdir -p "$HOME/.config/pi-web"
  (set -C; printf '%s\n' "$host" > "$file")
  printf '域名配置已保存。\n'
)
```

再创建启动脚本。它读取这个域名，启动时让你输入密码，密码不写入磁盘；最后调用已安装的 `pi-web`，脚本里不写版本：

```bash
(
  set -eu
  umask 077
  script="$HOME/.local/bin/pi-web-start"
  test ! -e "$script" || {
    printf '启动脚本已存在，未覆盖。\n'
    exit 1
  }
  mkdir -p "$HOME/.local/bin"
  set -C
  cat > "$script" <<'EOF'
#!/usr/bin/env bash
set -eu
PI_WEB_ALLOWED_HOSTS=$(cat "$HOME/.config/pi-web/allowed-hosts")
test -n "$PI_WEB_ALLOWED_HOSTS" || {
  printf '域名配置为空，未启动。\n'
  exit 1
}
export PI_WEB_ALLOWED_HOSTS
read -r -s -p 'Pi Web 密码：' PI_WEB_PASSWORD
printf '\n'
test -n "$PI_WEB_PASSWORD" || {
  printf '密码为空，未启动。\n'
  exit 1
}
export PI_WEB_PASSWORD
exec pi-web --hostname 127.0.0.1 --port 30141 --no-open
EOF
  chmod u+x "$script"
  bash -n "$script"
  printf '启动脚本已准备好；尚未启动。\n'
)
```

接着停止旧实例，用脚本重新启动。停止会中断旧实例里正在运行的任务和网页终端，已保存的会话不会删除；先等回复结束。如果你正在 Pi Web 里看本文或和智能体对话，先把下面的命令记下来：

```bash
pi-web stop --port 30141 &&
  "$HOME/.local/bin/pi-web-start"
```

前半段失败时不会继续启动；第一次安装后还没运行 Pi Web 的，只执行第二行。出现 `Pi Web 密码：` 时输入密码管理器里的密码，字符不显示是正常的。保持这个终端窗口打开；关窗口或按 `Ctrl+C` 都会停止 Pi Web。`pi-web stop` 找不到实例时，先运行 `pi-web status` 核对，不要用模糊的批量结束进程命令。

本机浏览器没有出现登录页，不一定说明密码没生效：之前用同一个密码登录过的话，30 天内的登录凭证仍然有效。示例环境里，本机浏览器直接进入了会话，下面两项检查都通过，手机无痕窗口则先出现了登录页。浏览器若弹出自带的“用户名 + 密码”框，点取消，关掉旧标签页再重新打开，在 Pi Web 自己的登录页只输密码。

验证的是正在运行的服务，不是终端里还留着一个变量。新开一个 WSL 终端，执行下面的只读检查；它不带浏览器的登录状态，也不调用模型：

```bash
curl -q --noproxy '*' --fail --silent --show-error --max-time 10 \
  http://127.0.0.1:30141/api/web-auth
```

预期返回：

```json
{"enabled":true,"authenticated":false}
```

`enabled: true` 才表示当前服务已开启密码；`authenticated: false` 表示这次检查没有登录，正是预期结果。再确认未登录请求拿不到会话：

```bash
curl -q --noproxy '*' --silent --show-error --max-time 10 \
  --output /dev/null --write-out 'HTTP %{http_code}\n' \
  http://127.0.0.1:30141/api/sessions
```

预期是 `HTTP 401`（需要认证），不是 `HTTP 200`。若 `enabled` 是 `false`、会话接口没有拒绝未登录请求，或者检查失败，先不要开启 Serve；在启动窗口按 `Ctrl+C` 停止这次启动，再核对域名文件和脚本。已经有远程入口的，先按文末停用步骤关闭本教程那一条 Serve，再处理密码。终端的 Ready 和浏览器能打开页面，都不能替代这一步。

</details>

<details>
<summary>A2.3 · Windows：开启并核对 Serve</summary>

回到 Windows PowerShell：

```powershell
tailscale serve status
```

如果已有其他 Serve 服务，先停在这里，避免占用其端口或覆盖路由。没有冲突再开启：

```powershell
tailscale serve --bg --https=443 http://127.0.0.1:30141
tailscale serve status
```

按提示完成 HTTPS 授权。报 `error enabling https feature … context deadline exceeded` 时，是命令行连 Tailscale 服务器超时，重试多半一样；去控制台 DNS 页面开启 HTTPS Certificates，等一两分钟再运行上面的命令。记录命令打印的完整 `https://…ts.net` 地址。使用的是 [`serve`](https://tailscale.com/docs/reference/tailscale-cli/serve)，只对自己的设备开放；不是面向公网的 [`funnel`](https://tailscale.com/docs/features/tailscale-funnel)。也不需要单独管理证书文件。

</details>

<!-- 拍摄点 A2：Serve 输出里保留 within your tailnet 与 HTTPS 地址；遮住身份信息。 -->

### A3 · 手机关掉 Wi-Fi，打开刚才的网址

用蜂窝网络，保持 Tailscale 开启、其他 VPN 应用关闭，在 Chrome 打开 Serve 打印的 HTTPS 地址。关 Wi-Fi 只是为了这次验收：手机和电脑在同一个 Wi-Fi 里时，连上不能证明出门也能用。验收通过后，日常开着 Wi-Fi 照常使用。能看到 Pi Web 登录页、输入密码后看到自己的会话，才算跨网访问成功。浏览器若提示证书错误，先停下排查地址，不点击忽略。

第一次只查看会话。准备好承担模型调用费用后，再发送一句“只回复连接成功，不使用工具”，观察回复是否逐步出现。暂时关闭手机 Tailscale，用新开的无痕窗口重试：应无法连接。这验证的是私有入口，不是页面缓存。

在 Chrome 菜单选择“安装应用”或“添加到主屏幕”。这个 PWA（Progressive Web App，可安装的网页应用，[安装说明](https://web.dev/learn/pwa/installation)）只是更方便的入口，电脑仍需开机，手机仍需联网并连接 Tailscale。

部分 Android 手机要先在系统设置里给 Chrome 开启“创建桌面快捷方式”权限，否则添加会失败。提示安装成功却在桌面上找不到时，去应用抽屉或全局搜索里找“Pi Web”。各家系统在这里的表现不一致，快捷方式能打开就够用。

<!-- 拍摄点 A3：蜂窝网络下的手机登录页／非敏感测试会话，以及桌面图标；成功后才替换为真实图片。 -->

这条路线走通就结束。下面的 B 不用配置。

<a id="route-b"></a>

## B：手机 VPN 不能关时，用固定网址访问

手机必须同时开着另一个 VPN 类应用时，可以用 Cloudflare 的固定隧道加一道登录门（Tunnel + Access）。它不占手机的 VPN 名额，但网页内容会在 Cloudflare 解开；如果你要的是“只在自己电脑上解开”，这条不是同等的替代。

这条路线也是三件事：先限制谁能进 → 连接本地服务 → 用手机验收。

![路线 B 示意：手机通过 HTTPS 到 Cloudflare，Access 先验证身份；Cloudflare 解开后经加密隧道送到电脑，cloudflared 再校验 Access 凭证；不是操作截图。](/images/posts/pi-web-on-phone/cloudflare-path.svg)

<details>
<summary>B1 · 先为网址加一道只允许本人的门</summary>

准备一个由 Cloudflare 托管的域名。下文用 `pi.example.com` 代表你的子域名；换成自己的，选一个还没有 DNS 记录的子域名，不修改根域名或已有网站的解析。

有多个 CF 账号时，在浏览器中选择包含目标域名的账号，确认 Zone（域名区域）、Tunnel 和 Access 的归属。Windows／WSL 中已有的 `cert.pem`、Wrangler 登录或环境变量，不证明当前属于正确账号。

在该账号创建或进入 Zero Trust，选择适用套餐，确认结算金额；单人试验先看 Free，不顺手开启付费增值项。管理账号开启双重验证。

在 Access → Applications 新建自托管 Web 应用，覆盖整个 `pi.example.com`，不要只保护首页。Allow 策略用 Emails 精确填写本人邮箱，未匹配者保持拒绝。不要选 Everyone，也不要把“使用邮箱验证码登录”本身当成授权名单；不要添加 Bypass。官方把“包含所有人”“包含所有有效邮箱”列为常见误配置，这样设等于谁都能进（[Access 策略说明](https://developers.cloudflare.com/cloudflare-one/access-controls/policies/)）。

登录方式可用本人身份提供商或邮箱验证码；敏感入口进一步开启可用的多因素认证。可以先从 24 小时会话有效期开始，之后按实际需求调整。手机无需安装 WARP。

保存 Access 后再创建公开路由。这个顺序能避免“隧道已经连通，登录保护还没建”的空档。

</details>

<!-- 拍摄点 B1：正确域名与整个主机名的 Access 策略，邮箱和账号信息脱敏。 -->

<details>
<summary>B2 · 独立隧道连接电脑，不动旧账号凭据</summary>

在 Pi Web 所在的 WSL 中，按 [Cloudflare 官方下载说明](https://developers.cloudflare.com/tunnel/downloads/)安装 `cloudflared`。本例把它与 Pi Web 放在同一个环境，不依赖 Windows 到 WSL 的转发。

先给 Pi Web 配置 `PI_WEB_ALLOWED_HOSTS=pi.example.com` 和非空 `PI_WEB_PASSWORD`，保留 `127.0.0.1:30141` 监听。密码可使用 A2 中的静默输入方法；先等任务结束，停止原实例，再以相同用户和确认过的版本重启。不要把网站密码和模型 API Key 混用。

在已确认的 CF 账号中创建控制台管理的 Tunnel，取名如 `pi-web`。运行它只需要该隧道的专用 Token（运行凭据），不需要执行 `cloudflared tunnel login`，也不用替换旧 `~/.cloudflared/cert.pem`。

先以前台进程验证，不直接复制 `service install` 覆盖已有服务。以下是 WSL Bash 的独立凭据保存方式；目录已存在就停下核对，不覆盖它：

```bash
(
  set -eu
  umask 077
  mkdir -p "$HOME/.config"
  mkdir "$HOME/.config/pi-web-tunnel"
  read -r -s -p '粘贴这条 Tunnel 的 Token：' TOKEN
  printf '\n'
  test -n "$TOKEN" || { printf 'Token 为空，已停止。\n'; exit 1; }
  printf '%s' "$TOKEN" > "$HOME/.config/pi-web-tunnel/token"
  unset TOKEN
)
```

不要在录屏时粘贴 Token；终端静默输入不能防止录到控制台原文、剪贴板历史或密码管理器。

用独立配置路径避免误读旧的默认配置。下面第一行只在第一次运行时创建空配置，之后重复执行也不会改动它；Token 不出现在命令参数里：

```bash
test -e "$HOME/.config/pi-web-tunnel/empty.yml" || printf '{}\n' > "$HOME/.config/pi-web-tunnel/empty.yml"
env -u TUNNEL_TOKEN -u TUNNEL_ORIGIN_CERT \
  cloudflared tunnel \
  --config "$HOME/.config/pi-web-tunnel/empty.yml" \
  run --token-file "$HOME/.config/pi-web-tunnel/token"
```

`--token-file` 需要 cloudflared 2025.4.0 或更新版本；其他旧的 `TUNNEL_*` 配置也应在专用进程环境中核对，不打印含秘密的完整环境变量。按控制台显示确认新隧道在线。

为这条隧道添加 Published application 路由：

| 配置项 | 本文例子 |
| --- | --- |
| Hostname | `pi.example.com` |
| Service URL | `http://127.0.0.1:30141` |
| Protect with Access | 开启，选择刚才的 Access 应用及其 Audience |

开启 Protect with Access 后，cloudflared 会再核对一次 Access 发的登录凭证。就算 Access 应用哪里配错，或者有别的路由也指向这条隧道，没有有效凭证的请求也到不了 Pi Web（[校验 Access 凭证](https://developers.cloudflare.com/cloudflare-one/access-controls/applications/http-apps/authorization-cookie/validating-json/)）。

保留公开 Host，不把 HTTP Host Header 强制改成 localhost。Pi Web 会核对请求上的地址是不是它认可的，对不上就拒绝发消息等操作。表里的 `http://` 只是电脑内部的最后一段；手机到 Cloudflare、Cloudflare 到电脑都是加密的。

不要使用临时 Quick Tunnel（随机 `trycloudflare.com` 地址）。[官方文档](https://developers.cloudflare.com/tunnel/get-started/quick-tunnels/)说明它不支持 SSE（Server-Sent Events，Pi Web 流式回复与终端使用的事件流）。也不用增加 Worker、Nginx 或路由器端口映射。

Token 是这条隧道的钥匙，拿到它就能运行这条隧道（[Tunnel Token 说明](https://developers.cloudflare.com/tunnel/reference/tunnel-tokens/)）。单独的目录防止和别的账号混用，`0600` 让电脑上其他系统用户读不到；但同一用户下运行的智能体仍能读到它。长期运行前，应考虑独立服务身份／凭据隔离，权限和已有服务核对后再配置自启。

</details>

<!-- 拍摄点 B2：路由的 Hostname、Service URL 与 Protect with Access；不截 Token 命令或二维码。 -->

<details>
<summary>B3 · 用一次拒绝和一次成功验收</summary>

先开无痕窗口访问 `https://pi.example.com/`，预期是 Access 登录页；在未登录窗口直接访问 `/api/sessions` 也不能得到会话数据。非授权身份应被拒绝。只有账号没有密码并不安全，不能因首页出现登录页就省掉 API 验收。

本人通过 Access 后，还需通过 Pi Web 密码。先查看会话，再按 A3 的方式做一次非敏感、小成本回复测试。

在手机上分别测试其他 VPN 应用关闭与开启，再切换 Wi-Fi／蜂窝网络。两种状态用同一网址，但实际走的网络不同；若直连 CF 不畅，应记录具体网络，不把“开着 VPN 应用能用”写成“随时稳定”。

Chrome 安装到桌面后再测试一次登录返回，以及锁屏后重新打开。Access 到期时先重新访问首页完成登录；不要为修登录问题放行 `/api/*`。首次成功后即可停下，不急着配置通知和自启动。

</details>

<!-- 拍摄点 B3：未登录时的门禁与本人登录后的测试页，两张图保留地址栏，隐藏私人会话。 -->

## 以后每次开机，怎么重新用起来

第一次配好之后，电脑重启或关机再开，大部分东西会自己回来，只有 Pi Web 要手动启动。下面按路线 A 写。示例环境已用 A2.2 的脚本完成一次手动切换，手机无痕窗口验证过登录；Windows 重启后的完整恢复，还没有在示例环境里按这一节走过一遍。

| 开机后 | 会不会自己回来 |
| --- | --- |
| Windows 上的 Tailscale | 会，但默认要等你登录 Windows 才连上（[无人值守模式说明](https://tailscale.com/docs/how-to/run-unattended)） |
| Serve 的 HTTPS 地址 | 会。用 `--bg` 开启的 Serve 在重启后自动恢复（[Serve 文档](https://tailscale.com/docs/reference/tailscale-cli/serve)） |
| WSL 和 Pi Web | 不会。Pi Web 每次都要在 WSL 里手动启动 |
| 手机上 Pi Web 的登录 | 只要启动时用同一个密码，30 天内不用重新登录 |

最后一行来自 Pi Web 0.12.0 和 0.12.2 的源码：登录凭证用 Pi Web 密码签名，有效期 30 天。所以每次启动都输入同一个密码；换了密码，所有已登录的设备都要重新登录。

### 每次开机后

1. 登录 Windows。PowerShell 里不用做任何事；想确认的话，运行 `tailscale serve status`，应该仍然显示那个 `https://…ts.net` 地址指向 `http://127.0.0.1:30141`。
2. 打开 WSL 终端，运行 A2.2 创建的启动脚本，输入 Pi Web 密码：

   ```bash
   pi-web-start
   ```

   提示找不到命令时，用完整路径 `~/.local/bin/pi-web-start`。在电脑浏览器里打开 `http://127.0.0.1:30141`，并按 A2.2 的两项只读检查确认 `enabled: true` 和 `HTTP 401`，才继续用手机访问。这个终端窗口可以最小化，不要关：关掉窗口或按 `Ctrl+C`，Pi Web 就停了。
3. 手机上打开 Tailscale，确认已连接，再点桌面上的 Pi Web 图标。

关机前不用做额外的事。Pi Web 正在回复时，等它答完再关机：重启会打断正在进行的回复，会话记录还在。

升级 Pi Web 单独做，不放进每次开机：先运行 `pi-web update --check` 查看是否有新版本。要升级时等任务结束，依次执行 `pi-web stop --port 30141`、`pi-web update`，用 `pi-web version` 确认版本，再运行 `pi-web-start` 和 A2.2 的检查。本文没有实测升级过程。

路线 B 多一步：在另一个 WSL 终端里重新运行 B2 中那条 `cloudflared tunnel … run --token-file …` 命令。它也是前台进程，窗口关了，隧道就断。

人在外面时，电脑如果被系统更新重启，Pi Web 不会自己回来，只能等你回到电脑前。出门久的话，提前在 Windows 更新里设好使用时段，或者暂停更新。在 Tailscale 托盘菜单的 Preferences 里勾选 Run unattended，能让 Tailscale 在没人登录 Windows 时也保持在线，但它只管 Tailscale，Pi Web 仍要手动启动。让 Pi Web 开机全自动启动，就得把密码存在磁盘上，同一用户下运行的智能体也能读到它，本文不展开。

<details>
<summary>隔一段时间要重新登录的几样东西</summary>

| 什么 | 多久 | 到期时 | 怎么办 |
| --- | --- | --- | --- |
| Tailscale 设备密钥 | 默认 180 天 | 这台设备掉线 | 在那台设备上重新登录 Tailscale |
| Pi Web 登录 | 30 天，或换了密码 | 回到登录页 | 输入 Pi Web 密码 |
| 模型账号（订阅登录或 API Key） | 看提供商 | Pi Web 回复时报认证错误 | 在电脑上打开 Pi Web 的 Models 面板，重新登录或重填 Key |
| HTTPS 证书 | Let's Encrypt 证书 90 天到期 | 手机提示证书错误 | 见下文 |
| B：Access 登录 | 你设的会话时长 | 跳到 Access 登录页 | 重新登录 |

电脑的密钥到期时，人在外面修不了：电脑已经掉线，手机连不上它。可以在 Tailscale 控制台的 Machines 页，从电脑那一行的菜单里选 Disable Key Expiry，让它不再到期（[密钥过期说明](https://tailscale.com/docs/features/access-control/key-expiry)）。代价是这台电脑的密钥不再自动作废，Tailscale 账号本身的安全就更要紧。不关的话，到期前回到电脑前重新登录一次。官方提醒，用 `tailscale up --force-reauth` 续期会短暂断线，不要隔着远程连接执行。

模型凭据和终端里的 Pi 共用，默认存在电脑的 `~/.pi/agent` 下（[Pi Web 中文说明](https://github.com/agegr/pi-web/blob/main/docs/README.zh-CN.md)），在 Pi Web 或终端里重新登录都可以。

证书由 Serve 自动签发，不用自己管理证书文件；官方文档没有写明 Serve 怎样续期，示例环境也还没用满 90 天。手机若提示证书错误，不要点忽略，先在电脑上运行 `tailscale serve status`，确认地址和服务都还在。

</details>

<details>
<summary>手机丢了、忘了密码、电脑重装：从哪一步重新开始</summary>

- 忘了 Pi Web 密码：Pi Web 不保存密码。先运行 `pi-web stop --port 30141`，再运行 `pi-web-start` 输入新密码；所有设备都要用新密码重新登录。
- 手机丢了：先在 Tailscale 控制台的 Machines 页删除这部手机，它就连不进你的网络了；再用新密码重启 Pi Web，旧手机上的登录随即失效，因为它的凭证是用旧密码签的。走路线 B 的，还要在 Cloudflare Zero Trust 里撤销这个用户的登录会话。
- 换了新手机：装 Tailscale、登录同一账号，打开电脑的 `ts.net` 地址登录 Pi Web，再到控制台删掉旧手机。
- 电脑重装系统，或 Tailscale 被登出：重新登录后，电脑在控制台里可能算一台新设备，名字也可能变，`ts.net` 地址就跟着变。把新地址写进 `~/.config/pi-web/allowed-hosts`，重新执行 A2 里开启 Serve 的那两条命令，再删掉控制台里的旧设备。
- `~/.pi/agent` 丢了：模型要重新登录或重填 Key。会话记录也存在这个目录里（[Pi Web 说明](https://github.com/agegr/pi-web/blob/main/README.md)），所以它值得备份；备份里有凭据，要像密码一样保管。

</details>

## 连上以后，再解决这些事

<details>
<summary>组网就不受网络影响了吗？</summary>

仍然受影响。Tailscale 会先试着让两台设备直连；网络限制让直连不成时，改走加密中继（[连接类型说明](https://tailscale.com/docs/reference/connection-types)）。中继打不开内容，但可能更慢；直连也受手机信号、运营商线路和家里上行带宽影响。

手机保持在线，从电脑执行 `tailscale status`，或对手机设备名执行 `tailscale ping`。`direct` 表示直连，`relay`／`peer-relay` 表示经过中继。刚开始先经过中继、随后转直连是正常现象。先看真实路径，不先关防火墙、开路由器端口或购买服务器。

在国内用，经过中继时常常更慢：截至 2026 年 10 月，Tailscale 的默认中继没有设在大陆，最近的在香港、东京和新加坡（[中继说明](https://tailscale.com/docs/reference/derp-servers)）。`tailscale status` 里中继后面跟着区域代码，比如 `hkg` 是香港、`tok` 是东京。长期走中继又嫌慢，有两个方向，本文都没有实测：

- Tailscale 的 [Peer Relay](https://tailscale.com/docs/features/peer-relay)：让你网络里一台网络条件好、常开的设备当中继。它不能是手机，要 Tailscale 1.86 以上，并开放一个其他设备能访问的 UDP 端口。连接仍在同一个账号和访问规则里，本文的安全说法不变。
- 换用能走国内共享节点的组网工具，比如国内开发者做的开源工具 [EasyTier](https://easytier.cn/index.html)。代价不小：安卓不 root 时，它也要通过系统 VPN 接口工作，同样占 VPN 名额；没有现成的 HTTPS 地址，Pi Web 要改监听地址，PWA 安装和推送通知也用不了；知道网络名和密钥的设备都能进这个网络。用别人提供的共享节点时，要开启它的[安全模式](https://easytier.cn/guide/network/secure-mode.html)并锁定节点公钥，否则文档没有保证中继看不到内容。

临时关掉手机上的 VPN 应用，和手机完全没有互联网，是两回事。前者 A 仍可能连上；后者任何远程方案都不能用。CF、向日葵、UU 远程也没有免于网络影响。

</details>

<details>
<summary>电脑休眠、手机锁屏、任务会怎样？</summary>

电脑、WSL、Pi Web 都需要继续运行；锁屏与睡眠不同。日常使用前核对插电时的睡眠策略，不必把屏幕一直点亮。重启以后哪些会自己回来、哪些要手动启动，见上面「以后每次开机，怎么重新用起来」。

Pi Web 已被服务端接收的智能体任务不要求手机一直亮屏，返回后有状态恢复机制；这不代表重启 Pi Web 进程后任务也能无损继续。

网页终端另有生命周期：本文核对版本中，最后一个客户端断开约 120 秒后会回收。长时间终端工作可用 tmux（保持终端会话的工具），不要靠手机永不锁屏维持任务。

</details>

<details>
<summary>用哪种方式，费用和隐私更合适？</summary>

Tailscale 的 Personal 计划免费，官方写明只适合非商业用途（[价格页](https://tailscale.com/pricing)）；Serve 提供设备域名，不需要购买自有域名，工作用途按实际套餐条款确认。CF 个人规模可先用 Free，已有域名时不必买服务器，域名续费和模型调用费仍然存在。免费套餐不是无限资源或可用性承诺。

A 的网页内容在自己的电脑上解开；Tailscale 仍知道账号、设备和连接记录。B 的网页内容先在 CF 解开；应用密码和 Access 管的是谁能进，不会让 CF 看不到经过它的内容。需要公司或客户数据授权时，先走组织审批，不能把“只有我用”当成合规结论。

通知可以晚点开。Pi Web 完成推送可能把会话标题显示在手机锁屏上，避免在标题里写客户名或敏感内容。使用云模型还需单独考虑模型提供商的数据政策；换一种接入方式不会改变模型拿到的输入。

</details>

<details>
<summary>打不开时，按这个顺序查；想停用也很简单</summary>

先查电脑本地地址，再查连接工具是否在线，最后查手机网络和浏览器登录，不一次改三层配置。

| 现象 | 先做什么 |
| --- | --- |
| 电脑本地地址打不开 | 检查原 Pi Web 进程，先不改隧道 |
| Windows 点登录后没反应 | 等十几秒看浏览器是否弹出；仍没有，再点一次 |
| A 在打开其他 VPN 应用后失联 | 关掉那个应用，重新连接 Tailscale |
| 重启 Pi Web 后弹出用户名和密码框 | 点取消，关掉旧标签页，重新打开 Pi Web 地址只输密码 |
| Serve 报 `error enabling https feature` | 在控制台 DNS 页面开启 HTTPS Certificates，稍等再运行 Serve |
| 电脑上打开 `ts.net` 地址提示连接被重置 | 电脑开着系统代理时，把 `*.ts.net` 加入代理绕过列表；手机不受影响 |
| A 设备在线但 HTTPS 拒绝 | 核对完整设备域名、Serve 状态、访问规则与 Pi Web 允许主机名 |
| B 返回 403 | 区分 Access 拒绝还是 Pi Web 的 Untrusted request，检查本人名单与 Host |
| B 返回 502／隧道离线 | 在 cloudflared 所在环境访问本机服务；确认隧道出站网络通畅 |
| 回复一次性出现或长操作超时 | 检查 SSE 是否被缓冲，单独测试手动压缩等长 POST，不反复重发有副作用的操作 |
| 登录后突然变成 HTML 错误 | 回首页重新完成 Access 登录，不取消 API 保护 |

停用 A 时，先看 `tailscale serve status`，确认 443 确实是本教程的服务，再关闭这一条：

```powershell
tailscale serve --https=443 off
```

不要用全局 reset 误删其他路由。停用 B 时，在自己的前台 cloudflared 窗口按 `Ctrl+C`，然后移除该子域名的隧道路由；保留 Access 到公开路由移除后，避免先撤门禁。Token 泄露需轮换并断开旧连接，不只是删除本地文件。

</details>

第一轮的完成标准很小：手机换到蜂窝网络后能登录、读到会话，且不该进入的人进不来。做到这里，再决定是否需要自启、通知和备用远程桌面。

写作说明：gpt-6-astra 起草、修订并绘制示意图；Claude Opus 5.5 补写开机恢复。

## 参考链接

- [Pi Web 中文说明](https://github.com/agegr/pi-web/blob/main/docs/README.zh-CN.md): 启动、密码、允许的主机名，以及远程访问要放在 HTTPS 或 VPN 后面；本文实测版本 0.12.2
- [Tailscale 的工作原理](https://tailscale.com/blog/how-tailscale-works): 登录服务器只交换公钥，私钥不离开设备
- [Tailscale DERP 中继](https://tailscale.com/docs/reference/derp-servers): 中继只转发已加密的数据，无法解密
- [Tailscale 连接类型](https://tailscale.com/docs/reference/connection-types): 直连、中继与性能边界
- [Tailscale Peer Relay](https://tailscale.com/docs/features/peer-relay): 用自己网络里的一台设备当中继；不能是手机，需要 1.86 以上和一个 UDP 端口
- [EasyTier 安全模式](https://easytier.cn/guide/network/secure-mode.html): 开启并锁定共享节点公钥后，中继无法解密；只开安全模式不能防冒充
- [Tailscale Serve](https://tailscale.com/docs/reference/tailscale-cli/serve): 只对自己网络内设备开放的 HTTPS 入口
- [Tailscale HTTPS 证书](https://tailscale.com/docs/how-to/set-up-https-certificates): 开启方法；设备名会写进公开的证书记录
- [Tailscale 与其他 VPN](https://tailscale.com/docs/reference/faq/other-vpns): iOS 与 Android 同时只能运行一个 VPN
- [Android VPN](https://developer.android.com/develop/connectivity/vpn): 同一用户或资料中只能有一个活动 VPN 服务
- [Tailscale 价格](https://tailscale.com/pricing): Personal 计划免费，仅适合非商业用途
- [Cloudflare Tunnel](https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/): 电脑主动连出，不开放入站端口
- [Cloudflare Access：发布自托管应用](https://developers.cloudflare.com/cloudflare-one/access-controls/applications/http-apps/self-hosted-public-app/): 先建访问策略，再发布隧道路由，并校验 Access 凭证
- [Cloudflare Access 策略](https://developers.cloudflare.com/cloudflare-one/access-controls/policies/): 包含所有人、所有有效邮箱属于常见误配置
- [Cloudflare Tunnel Token](https://developers.cloudflare.com/tunnel/reference/tunnel-tokens/): 拿到 Token 就能运行这条隧道；应定期轮换
- [Cloudflare Quick Tunnels](https://developers.cloudflare.com/tunnel/get-started/quick-tunnels/): 临时地址，不支持 SSE
- [向日葵远程控制](https://sunlogin.oray.com/): 远程桌面是另一种操作方式，不等于手机网页
- [网易 UU 远程](https://uuyc.163.com/): 支持手机控制电脑；本文不作厂商性能或端到端加密背书
