外網存取
預設情況下 雲瞰 只在區域網路可用——出門後手機 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 沒傳對。更多疑難排解見 疑難排解。
這篇文件有幫助嗎?