外网访问
預設情況下 雲瞰 只在區域網路可用——出門後手機 App 連不上家裡的伺服器。本章給出三種把 雲瞰 安全地暴露到外網的方式:公網直連(推薦)、組網、反向代理,並說明各自要開哪些連接埠。公網直連只需要一個網域和路由器上的一條規則,憑證在網頁裡自動申請。
先看這一條
24214 / 23880)絕對不要轉發到公網——它們沒有登入這一層,轉出去等於把攝影機直接交給全網的掃描器。三種方式怎麼選
| 方式 | 需要準備 | 路由器上要做什麼 | 即時畫面延遲 | 適合誰 |
|---|---|---|---|---|
| 公網直連 | 一個網域(可用免費子網域),憑證網頁裡自動申請 | IPv4:轉發 1 條 TCP 規則;IPv6:放行入站即可 | WebRTC 秒開,低於 0.5 秒 | 寬頻有公網 IP 或 IPv6(推薦) |
| 組網(VPN) | 每台要用的裝置裝同一款組網 App | 什麼都不用做 | WebRTC 秒開 | 電信業者大內網且沒有 IPv6,或不想開任何連接埠 |
| 反向代理 + 網域 | 已經在跑 nginx / Caddy / Lucky | 轉發反向代理用的連接埠 | 預設 HLS 約 2.5 秒(可透傳後走 WebRTC) | 已有反向代理、想統一用 443 的使用者 |
三者不衝突,可以同時開。拿不準就按這個順序試:先看寬頻有沒有 IPv6——有的話公網直連連連接埠都不用轉;沒有 IPv6 但有公網 IP,同樣用公網直連;兩樣都沒有(電信業者大內網)就用組網。
方式一:公網直連(推薦)
雲瞰 把網頁、App 介面和即時畫面全部收斂到一個 TCP 連接埠上:同一個連接埠既走 HTTPS,也承載 WebRTC 的即時視訊通道,由伺服器端自動分流。所以路由器上只有一條規則,不需要再為 WebRTC 單獨轉發 UDP 連接埠。
這條路解決了什麼
第 1 步:網域與憑證(網頁裡自動申請)
公網直連必須用 HTTPS:手機 App 會拒絕不受信任的自簽憑證,所以需要一個你自己的網域和一張真憑證。在 設定 → 一般 → HTTPS 憑證 裡填網域和 DNS 服務商的 API key,儲存後 雲瞰 會自動向 Let's Encrypt 申請憑證,並在到期前自動續期。
- 不需要開放 80 連接埠:走的是 DNS-01 驗證(在你的網域下暫時寫一條 TXT 記錄),即使 80 入站被電信業者封鎖也照樣能簽。
- 支援的 DNS 服務商:阿里雲、騰訊雲 DNSPod、華為雲、Cloudflare、AWS Route 53、Porkbun、deSEC、DuckDNS。
- 沒有自己的網域也能用:deSEC 和 DuckDNS 免費送子網域,註冊後就能當自己的網域使用。
- 已經有憑證的話,把
fullchain.pem和privkey.pem放進data/certs/即可,雲瞰 只會用它、不會覆蓋你的檔案。 - 填完可以先點自我檢查:它會真的寫一條測試記錄再刪掉,確認 API key 和權限沒問題,而且不消耗 Let's Encrypt 的申請次數。
網域要你自己有
第 2 步:選一個公網連接埠
家用寬頻建議避開 443
23443 就很合適;雲端主機或其它地區的寬頻沒有這個限制,可以用 443——好處是存取網址不用帶連接埠號。在同一個面板裡填公網連接埠,儲存即生效:後端會當場重新設定並確認連接埠真的進入監聽狀態,不需要重啟容器,也不用改 compose 檔案。連接埠如果被別的程式占用、或者和內部連接埠衝突,儲存時就會當場擋下來告訴你,而不是存下去再悄悄失敗。
第 3 步:路由器
- IPv4(有公網 IP):加一條連接埠轉發,把公網的
23443/tcp轉到 雲瞰 主機的23443。只要這一條——不需要轉發23515或其它連接埠。 - IPv6:IPv6 沒有 NAT,不用轉發,只要在路由器的 IPv6 防火牆裡放行到本機
23443/tcp的入站連線。 - 網域解析:公網 IPv4 填 A 記錄、IPv6 填 AAAA 記錄。家用寬頻的 IP 會變,設定一個 DDNS 用戶端自動更新即可。
第 4 步:驗證
用 https://<你的網域>:23443/ 打開網頁,登入後進即時監控。畫面應該在 1 秒內出來,並且走的是 WebRTC 通道。
同一個面板裡還有公網可達性自我檢查:它會檢查網域解析到的位址、連接埠通不通、憑證對不對,並直接告訴你卡在哪一步——不用自己抓封包猜。
電信業者沒給公網 IP 怎麼辦
方式二:組網(不開任何連接埠)
组网工具(Tailscale / WireGuard / ZeroTier 等)在你的手机和家里的服务器之间建一条加密隧道,手机像在家一样用内网地址访问 云瞰——不需要在路由器上开任何端口,服务器完全不暴露到公网。
- 1
服务器装组网客户端
在跑 云瞰 的那台机器(或同 LAN 的软路由)装 Tailscale,然后
tailscale up登录。WireGuard / ZeroTier 同理。bashcurl -fsSL https://tailscale.com/install.sh | sh sudo tailscale up - 2
手机装同款 App 并登录同一账号
手机端装对应的 Tailscale / ZeroTier App,登录与服务器相同的账号,两端就进了同一个虚拟局域网。
- 3
App 里填组网地址
云瞰 App 的服务器地址填组网分配的 IP——Tailscale 是
100.x.x.x,ZeroTier 是10.x.x.x,端口仍然是:23406,例如http://100.x.x.x:23406。 - 4
完成
出门后手机连 4G/5G 也能访问,体验和在家一样。无需公网 IP、无需域名、无需 HTTPS 证书。
組網的取捨
方式三:反向代理 + 網域
如果你有公网 IP + 一个域名,想用 https://cam.example.com 这种地址访问(方便发给不方便装组网 App 的家人),可以在 云瞰 前面架一层 nginx / Caddy 做 HTTPS 终止。云瞰 容器内部已自带一层 nginx,外层反代只需把流量整体转发给 :23406。
反向代理場景下即時畫面走什麼
stream 區塊、Caddy 的 layer4 外掛)把某個連接埠整體透傳給 雲瞰 的公網單一連接埠即可——兩者可以共存:網頁走 443 的反向代理,即時畫面走透傳連接埠。nginx 配置
把下面内容整份存为 /etc/nginx/sites-available/skyview.conf,替换 <你的域名> 后 ln -s 到 sites-enabled/ —— 一个文件搞定,不用再单独建 snippet。两个 location / 里的 proxy_set_header 块完全一样:nginx 的 proxy_set_header 是覆盖不继承,每个 location 必须各带一份,照抄即可。
# WebSocket Upgrade 透传 —— 必须在 http {} context
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 80;
listen [::]:80;
server_name <你的域名>;
# certbot --nginx 会自动把此块改成 301 跳 https
location / {
proxy_pass http://127.0.0.1:23406;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port $server_port;
proxy_set_header X-Forwarded-Proto $scheme; # ★ 漏了它 HTTPS 部署会登录死循环
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_buffering off;
proxy_connect_timeout 60s;
proxy_send_timeout 1d;
proxy_read_timeout 1d;
}
}
server {
listen 443 ssl;
listen [::]:443 ssl;
http2 on;
server_name <你的域名>;
ssl_certificate /etc/letsencrypt/live/<你的域名>/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/<你的域名>/privkey.pem;
# 录像导出、人脸库批量导入可能上百 MB;长 JWT cookie 较大防 414
client_max_body_size 200m;
client_header_buffer_size 4k;
large_client_header_buffers 8 16k;
location / {
proxy_pass http://127.0.0.1:23406;
proxy_http_version 1.1;
# ↓ 与上面 :80 location 完全相同,逐行照抄(proxy_set_header 不继承)
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port $server_port;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_buffering off;
proxy_connect_timeout 60s;
proxy_send_timeout 1d;
proxy_read_timeout 1d;
}
}X-Forwarded-Proto / X-Forwarded-Host 必须传 + 每个 location 都要 include
X-Forwarded-Proto 判断客户端真实 scheme、靠 X-Forwarded-Host 知道对外域名。漏 X-Forwarded-Proto → HTTPS 部署下登录后立刻被踢回登录页、直播画面被浏览器当 Mixed Content 拦掉;漏 X-Forwarded-Host → 录像回放 / 下载 / 导出的链接拼成错误地址,客户端打不开。上面的 nginx 模板和 Caddy(默认即传这两个头)都已覆盖,照抄即可——端口无需手动配 X-Forwarded-Port,容器内 nginx 会自动按「外层反代 / 裸 IP 直连」两种场景兜好。另外 nginx 的 proxy_set_header 是覆盖不是追加:某个 location 只要写了任意一条,上层的 set_header 全部失效——所以每个 location 都要 include 完整那一份 snippet。申请证书(机器要能从公网访问 :80):
certbot --nginx -d <你的域名> -m <你的邮箱> --agree-tos --no-eff-email --redirectCaddy 配置(更简单)
Caddy 自动签发 / 续期 Let's Encrypt 证书,无需 certbot,配置短很多。/etc/caddy/Caddyfile:
<你的域名> {
reverse_proxy 127.0.0.1:23406 {
# Caddy 默认就传 X-Forwarded-{For,Proto,Host}
# 长连接(对讲 WS / 事件 SSE / 直播流)调大 flush + 超时
flush_interval -1
transport http {
read_timeout 24h
write_timeout 24h
}
}
request_body {
max_size 200MB
}
}連接埠速查表
| 連接埠 | 協定 | 用途 | 外網是否要開 |
|---|---|---|---|
23443 | TCP | 公網直連:網頁 + 介面 + 即時畫面(HTTPS,連接埠可改) | 要開(公網直連方案下唯一要開的連接埠) |
23406 | TCP | 區域網路存取入口(明文 HTTP) | 不要開(要外網存取請用上面的 23443) |
23515 | UDP + TCP | 即時畫面媒體串流(區域網路 / 組網 / IPv6 直連) | 不用開 |
23880 | TCP | RTSP,供外部播放器在區域網路直連 | 不用開 |
24214 | TCP | 內部串流服務 | 禁止開 |
別把內部連接埠轉出去
24214(內部串流服務)和 23880(RTSP)沒有登入這一層——直播串流的驗證做在 23406 / 23443 的入口上。把它們裸轉到公網會直接繞過驗證,等於公開你的攝影機畫面。同理也不要把 23406 裸轉出去:那是明文 HTTP,密碼在公網上是明文傳輸的,瀏覽器和 Android 9+ 還會拒絕在明文頁面裡申請攝影機權限。要外網存取,用上面的公網直連(23443,HTTPS)。配完怎么验证
# 1. HTTP→HTTPS 跳转(反代场景)
curl -sSI http://<你的域名>/healthz | head -1 # 期望 301
# 2. 健康检查
curl -sS https://<你的域名>/healthz # 期望 {"code":0,...}
# 3. 未登录访问受保护接口
curl -sS -o /dev/null -w '%{http_code}\n' \
https://<你的域名>/api/cameras # 期望 401最后用浏览器走一遍完整流程:登录 → 实时监控看到画面 → 浏览器控制台没有 Mixed Content 报错。若登录后立刻被踢回登录页、或直播画面报 Mixed Content,99% 是 X-Forwarded-Proto 没传对。更多排查见 排错。