← 返回
未分类 Key

frp-tunnel

Share local development servers via self-hosted frp tunnel with custom domains and auto HTTPS. Use when you need to share localhost with others, demo to clie...
通过自托管 frp 隧道分享本地开发服务器,支持自定义域名和自动 HTTPS,用于与他人共享本地服务或演示。
darwin7381
未分类 clawhub v1.0.0 1 版本 100000 Key: 需要
★ 0
Stars
📥 641
下载
💾 0
安装
1
版本
#latest

概述

frp-tunnel — 自建內網穿透

用自建的 frp + Caddy + Hetzner VPS 分享本地開發中的網站。自訂域名、自動 HTTPS、無限 tunnel。

> 替代方案(無 VPS 時):見 old-share-local-site skill(ngrok/localhost.run)

⚙️ Config(自訂區塊)

使用此 skill 前,請將以下值替換成你自己的環境:

變數目前的值說明
---------------------
VPS IP5.223.75.160你的 VPS 公網 IP
域名*.tunnel.fud.city你的 wildcard 子域名
DNS ProviderCloudflare (fud.city)管理你域名 DNS 的服務
GitHub 備份https://github.com/darwin7381/frp-tunnel你的 private repo(可選)

> Dashboard 密碼:不記在這裡。在 VPS 的 frps.toml 裡設定你自己的密碼。

基礎資訊

項目
-----------
VPS IP5.223.75.160
VPS ProviderHetzner Cloud, Singapore, CPX12
域名*.tunnel.fud.city
DNSCloudflare (fud.city)
HTTPS 方式Wildcard cert (DNS-01 challenge via Cloudflare API)
frp Dashboardhttp://5.223.75.160:7500
GitHub 備份https://github.com/darwin7381/frp-tunnel (private)
本地 config~/.frp/frpc.toml
本地 tmux sessionfrpc
VPS servicesfrps (systemd) + caddy (systemd)

目前的 Tunnels

名稱本地 PortURL
---------------------
news-dashboard5173https://news.tunnel.fud.city
oldweb8080https://oldweb.tunnel.fud.city
api8000https://api.tunnel.fud.city
fin-terminal5177https://terminal.tunnel.fud.city
fin-terminal-api3002https://terminal-api.tunnel.fud.city

HTTPS 方式:Wildcard vs Per-Domain

VPS 上的 Caddy 有兩種方式為 tunnel 提供 HTTPS。目前使用 Wildcard(推薦)。

方式 A:Wildcard Certificate(目前使用 ✅)

原理:一張 *.tunnel.fud.city wildcard cert 涵蓋所有子域名。使用 DNS-01 challenge — Caddy 透過 Cloudflare API 自動加 TXT record 驗證。

Caddyfile:

*.tunnel.fud.city {
    tls {
        dns cloudflare {env.CF_API_TOKEN}
    }
    reverse_proxy localhost:8080
}

需要:

  • Caddy with Cloudflare DNS plugin(標準版沒有,需重新編譯或下載)
  • Cloudflare API Token(Zone:DNS:Edit 權限,只限 fud.city)
  • Token 設定在 /etc/systemd/system/caddy.service.d/override.conf

新增 tunnel 只需改本地 frpc.toml,不用動 VPS。

安裝步驟(已完成,僅供紀錄):

# 1. 下載帶 cloudflare plugin 的 Caddy
curl -s "https://caddyserver.com/api/download?os=linux&arch=amd64&p=github.com%2Fcaddy-dns%2Fcloudflare" -o /usr/bin/caddy
chmod +x /usr/bin/caddy

# 2. 驗證 module 存在
caddy list-modules | grep cloudflare

# 3. 設定 CF token
mkdir -p /etc/systemd/system/caddy.service.d
cat > /etc/systemd/system/caddy.service.d/override.conf << EOF
[Service]
Environment="CF_API_TOKEN=你的token"
EOF

# 4. 更新 Caddyfile
cat > /etc/caddy/Caddyfile << EOF
*.tunnel.fud.city {
    tls {
        dns cloudflare {env.CF_API_TOKEN}
    }
    reverse_proxy localhost:8080
}
EOF

# 5. 重啟
systemctl daemon-reload
systemctl restart caddy

方式 B:Per-Domain Certificate(備用)

原理:每個子域名獨立簽一張 cert。使用 HTTP-01 challenge — Let's Encrypt 訪問 http://xxx/.well-known/acme-challenge/ 驗證。

Caddyfile:

news.tunnel.fud.city {
    reverse_proxy localhost:8080
}

terminal.tunnel.fud.city {
    reverse_proxy localhost:8080
}

# 每加一個 tunnel 就加一段

需要:標準版 Caddy 即可,不需要 API token。

缺點:每次加 tunnel 都要 SSH 進 VPS 改 Caddyfile 再 reload Caddy。

適合場景

  • 不想給 Cloudflare API token
  • 需要不同子域名指向不同 upstream(不只是 frp)
  • 臨時測試

如何切換

Wildcard → Per-Domain:

ssh root@5.223.75.160
# 刪除 override.conf(或保留也無妨)
# 把 Caddyfile 改成逐一列出每個域名
systemctl restart caddy

Per-Domain → Wildcard:

按上面方式 A 的安裝步驟。

日常操作

啟動 frpc(Mac 重啟後需要)

SOCK=/tmp/openclaw-tmux/openclaw.sock
tmux -S $SOCK new-session -d -s frpc 'frpc -c ~/.frp/frpc.toml'

檢查 frpc 狀態

SOCK=/tmp/openclaw-tmux/openclaw.sock
tmux -S $SOCK capture-pane -t frpc -p -S -10

加新 tunnel(Wildcard 模式 — 只需改本地)

Step 1 — 本地 config

編輯 ~/.frp/frpc.toml,加一段:

[[proxies]]
name = "new-project"
type = "http"
localPort = 3000
customDomains = ["new.tunnel.fud.city"]

Step 2 — 重啟本地 frpc

SOCK=/tmp/openclaw-tmux/openclaw.sock
tmux -S $SOCK send-keys -t frpc C-c
sleep 2
tmux -S $SOCK send-keys -t frpc 'frpc -c ~/.frp/frpc.toml' Enter

Step 3 — 驗證(不需要動 VPS!Wildcard cert 自動涵蓋)

curl -sI https://new.tunnel.fud.city | head -5

確認回傳 HTTP/2 200 才告訴用戶。

Step 4 — 同步 GitHub 備份

cd ~/Projects/frp-tunnel
cp ~/.frp/frpc.toml ./frpc.toml
git add -A && git commit -m "Add tunnel: new-project" && git push

加新 tunnel(Per-Domain 模式 — 需改 VPS)

同上 Step 1-2,但在 Step 2 和 3 之間多一步:

ssh root@5.223.75.160
# 編輯 /etc/caddy/Caddyfile,加:
# new.tunnel.fud.city {
#     reverse_proxy localhost:8080
# }
systemctl reload caddy
# 等 30 秒讓 cert 簽發

前端 Vite 的 allowedHosts

Vite dev server 預設會拒絕非 localhost 的 Host header。加 tunnel 時前端 vite.config.ts 也要加:

server: {
  allowedHosts: ['xxx.tunnel.fud.city'],
}

臨時開一條(不改 config)

frpc http --server-addr 5.223.75.160:7000 --local-port 3000 --custom-domain temp.tunnel.fud.city

Wildcard 模式下,臨時 tunnel 也自動有 HTTPS。

發送 URL 前必做的檢查(SOP)

每次發送 tunnel URL 給用戶前:

  1. 確認 frpc 在跑tmux capture-pane -t frpc
  2. 確認 proxy 成功 — 看到 start proxy success
  3. 確認回傳 200 — 不是 502/404/連線失敗
  4. 確認內容正確curl -s https://xxx.tunnel.fud.city | grep ""</code>,title 必須是預期的網站名稱</li><li><strong>確認本地 server 是活的,不是僵屍進程</strong> — <code>ps aux | grep <port></code> 檢查進程啟動時間,如果是幾天前啟動的舊進程,很可能已經僵屍化(佔 port 但不正常服務,偶爾回 200 但頁面空白)。殺掉重啟。</li><li><strong>瀏覽器驗證時禁止信任 cache</strong> — 用無痕模式或 <code>curl --resolve</code> 強制走 VPS IP,不要用已開過的 tab</li></ol><h3>⚠️ 已知 Failure Mode:僵屍進程(2026-03-04 事故)</h3><p>本地 dev server(如 Vite)長時間不重啟會變成僵屍:佔著 port、偶爾回 HTTP 200,但實際頁面是空白。</p><p>curl 檢查看似正常,瀏覽器有 cache 也看似正常,但外部用戶(包含手機)打開是空白。</p><p><strong>排查步驟</strong>:</p><ol><li><code>ps aux | grep <port></code> — 看進程啟動時間,超過 1-2 天就該懷疑</li><li>殺掉舊進程,在 tmux 裡重新 <code>npm run dev</code></li><li>重啟後再 curl + grep title 確認</li></ol><h2>VPS 故障排除</h2><pre><code># SSH 進 VPS ssh root@5.223.75.160 # 檢查 frps systemctl status frps journalctl -u frps --no-pager -n 20 # 檢查 Caddy systemctl status caddy journalctl -u caddy --no-pager -n 20 # 檢查 Caddy modules(確認有 cloudflare) caddy list-modules | grep cloudflare # 檢查 cert 狀態 caddy cert list 2>/dev/null || journalctl -u caddy | grep "certificate obtained" # 重啟 systemctl restart frps systemctl restart caddy </code></pre><h2>防火牆規則(VPS ufw)</h2><table><thead><tr><th>Port</th><th>用途</th></tr></thead><tbody><tr><td>------</td><td>------</td></tr><tr><td>22</td><td>SSH</td></tr><tr><td>80</td><td>HTTP (Caddy + Let's Encrypt)</td></tr><tr><td>443</td><td>HTTPS (Caddy)</td></tr><tr><td>7000</td><td>frp client 連線</td></tr><tr><td>7500</td><td>frp Web Dashboard</td></tr></tbody></table><h2>費用</h2><ul><li>VPS: $7.59/月(Hetzner CPX12 Singapore + IPv4)</li><li>域名: 已有 fud.city</li><li>SSL: 免費(Let's Encrypt via Caddy)</li><li>流量: 0.5 TB/月(實際用量約 10-15 GB/月)</li></ul></div> </div> </div> <div id="tab-versions" class="detail-content"> <div class="detail-section"> <h2>版本历史</h2> <p style="margin-bottom:12px;font-size:14px;color:#94a3b8;">共 1 个版本</p> <ul class="version-list"> <li> <div> <span class="version-tag">v1.0.0</span> <span style="font-size:11px;color:#5b6abf;margin-left:8px;background:#eef0ff;padding:1px 8px;border-radius:10px;">当前</span> </div> <div style="font-size:12px;color:#94a3b8;"> 2026-05-02 03:50 安全 安全 </div> </li> </ul> </div> </div> <div id="tab-security" class="detail-content"> <div class="detail-section"> <h2>安全检测</h2> <div class="sec-grid"> <div class="sec-card"> <h4>腾讯云安全 (Keen)</h4> <div class="sec-status sec-safe"> 安全,无风险 </div> <a href="https://tix.qq.com/search/skill?keyword=264ec0f5ff724684b541453b661842d3" target="_blank">查看报告</a> </div> <div class="sec-card"> <h4>腾讯云安全 (Sanbu)</h4> <div class="sec-status sec-safe"> 安全,无风险 </div> <a href="https://static.cloudsec.tencent.com/html-report-v2/2026/05/26/425897_08049849b41cb0be9da9d923fce3ebee.html?q-sign-algorithm=sha1&q-ak=AKID8JMG1bzBC1dz96qNhssfFftujT1NCoFi&q-sign-time=1781413252%3B1812949252&q-key-time=1781413252%3B1812949252&q-header-list=host&q-url-param-list=&q-signature=22f7e6bb57630b05371fb542d04472e67696a094" target="_blank">查看报告</a> </div> </div> </div> </div> <!-- Recommended Skills --> <div style="margin-top:24px;"> <h2 style="font-size:18px;font-weight:600;margin-bottom:16px;">🔗 相关推荐</h2> <div class="rec-grid"> <div class="rec-card"> <span class="badge-cat" style="margin-bottom:8px;display:inline-block;">developer-tools</span> <h3><a href="/s/share-local-site">Share Local Site</a></h3> <div class="rec-owner">darwin7381</div> <div class="rec-desc">通过公网 URL 共享本地开发服务器。适用于向客户演示网站、让同事预览工作或测试移动端等场景。</div> <div class="rec-stats"> <span style="color:#f39c12;">★ 0</span> <span style="color:#5b6abf;">📥 552</span> </div> </div> <div class="rec-card"> <span class="badge-cat" style="margin-bottom:8px;display:inline-block;"></span> <h3><a href="/s/auto-reboot-recovery">Auto Reboot Recovery</a></h3> <div class="rec-owner">darwin7381</div> <div class="rec-desc">macOS 服務持久化與重啟恢復。管理 LaunchAgent、tmux bootstrap、wrapper daemon 三層架構,確保電腦重啟後所有服務自動恢復。適用時機:(1) 新增需要常駐的服務,(2) 把現有 tmux sessi</div> <div class="rec-stats"> <span style="color:#f39c12;">★ 0</span> <span style="color:#5b6abf;">📥 331</span> </div> </div> <div class="rec-card"> <span class="badge-cat" style="margin-bottom:8px;display:inline-block;"></span> <h3><a href="/s/service-persistence">Service Persistence</a></h3> <div class="rec-owner">darwin7381</div> <div class="rec-desc">macOS 服務持久化與重啟恢復。管理 LaunchAgent、tmux bootstrap、wrapper daemon 三層架構, 確保電腦重啟後所有服務自動恢復。Use when: (1) 新增需要常駐的服務,(2) 把現有 tmux</div> <div class="rec-stats"> <span style="color:#f39c12;">★ 0</span> <span style="color:#5b6abf;">📥 361</span> </div> </div> </div> </div> </div> <script> document.addEventListener('DOMContentLoaded',function(){ document.querySelectorAll('.detail-tab').forEach(function(btn){ btn.addEventListener('click',function(e){ var tab = this.getAttribute('data-tab'); document.querySelectorAll('.detail-tab').forEach(function(b){b.classList.remove('active')}); document.querySelectorAll('.detail-content').forEach(function(c){c.classList.remove('active')}); this.classList.add('active'); var el = document.getElementById('tab-'+tab); if(el) el.classList.add('active'); }); }); }); </script> <div class="footer"> <p>Skill工具集 © 2026</p> </div></body> </html>